Как определить, какой конструктор Autofac использует при разрешении - PullRequest
0 голосов
/ 25 мая 2018

Я использую пользовательский JsonConverter и JsonSerializerSettings.TypeNameHandling = TypeNameHandling.Objects для создания необходимых экземпляров во время десериализации.Экземпляры создаются путем разрешения типов из контейнера IOC Autofac.Все работает нормально, кроме ...

У меня есть несколько "базовых объектов", которые запрашивают уникальный идентификатор в конструкторе из службы (который правильно вводится в конструктор).При десериализации это не должно происходить, потому что это довольно дорого, и идентификаторы будут заполняться из файла Json в любом случае после создания экземпляра.

В настоящее время при разрешении из пользовательского JsonConverter я использую _scope.Resolve<T>(new TypedParameter(typeof(IIdService), null)); to then - в вызываемом конструкторе - проверьте на null и действуйте соответственно.

Некоторые люди, по-видимому, считают несколько конструкторов хуже, чем запах кода при использовании IOC (что заставляет меня задуматься, почему Autofac предлагает несколько функций, касающихсятема), но в контексте десериализации я думаю, что это может иметь смысл.

Насколько я могу судить, у Autofac есть механизмы, позволяющие решить, какой конструктор использовать при регистрации , но не когда разрешения .Моим предпочтительным решением было бы добавить пользовательский атрибут в конструктор (например, [CtorForDeserializing]) и использовать его для принятия решения.Это возможно?

Ответы [ 3 ]

0 голосов
/ 29 мая 2018

Существует несколько точек расширения, которые есть у Autofac для активаций на основе отражений, но пока недостаточно хорошо документированы, что может вам помочь: IConstructorFinder и IConstructorSelector. * Используется

IConstructorFinderнайти все доступные конструкторы по типу.Основным примером является DefaultConstructorFinder, который находит только открытые конструкторы.Если вы хотите, скажем, скрыть конструкторы с определенными атрибутами или начать поиск внутренних / частных конструкторов, вы можете создать собственный искатель.На самом деле это происходит только один раз, поэтому вы не можете здесь делать выбор во время выполнения.

IConstructorSelector используется для выбора, во время разрешения, какой конструктор должен использоваться для создания экземпляра объекта.В ядре Autofac есть несколько из них, но основным примером является MostParametersConstructorSelector, который выбирает конструктор, который имеет наиболее доступные параметры соответствия в то время.Конструкторы находят с помощью IConstructorFinder, а затем этот набор конструкторов представляет собой то, что представляется на IConstructorSelector на выбор.Здесь вы можете сделать больше выбора во время выполнения, поскольку это происходит каждый раз, когда объект разрешается.

Существуют методы расширения, которые помогут вам добавить ваш искатель / селектор в регистрацию:

builder.RegisterType<MyType>()
       .FindConstructorsWith(new MyConstructorFinder())
       .UsingConstructor(new MyConstructorSelector());

Вам не нужно настраивать обе вещи, вы можете просто сделать одну или другую, если хотите.Я просто показываю вам расширения.

0 голосов
/ 17 июня 2018

Трэвис Иллиг послал меня в правильном направлении - спасибо!

В итоге я реализовал решение, основанное на следующих деталях:

Реализация пользовательских атрибутов, например: public class DeserializeCtorAttribute : Attribute { }, что будетиспользуется (также будет реализовано) IConstructorFinder.

Реализация пустого универсального интерфейса, например: IDeserializable<T>, который будет использоваться для разрешения служб / компонентов.

Позволяетклассы компонентов реализуют интерфейс (MyClass : IDeserializable<MyClass>) и добавляют дополнительную регистрацию для компонента:

_builder.RegisterType<MyClass>().As<IDeserializable<MyClass>>()
                .FindConstructorsWith(MyConstructorFinder);

Используйте реализованный DeserializeCtorAttribute в нужном конструкторе MyClass.

ПустьJsonConverter создайте необходимый экземпляр, вызвав (MyClass) scope.Resolve(IDeserializable<MyClass>);литье обязательно, но безопасно.Благодаря регистрации экземпляр будет создан с использованием нужного конструктора.

0 голосов
/ 26 мая 2018

На самом деле Autofac может решить, какой конструктор использовать обоими способами - при регистрации или разрешении.Для части разрешения здесь есть цитата из документации: « Autofac автоматически использует конструктор для вашего класса с большинством параметров, которые можно получить из контейнера » ( см. Здесь ).

Рассмотрим следующий пример.

public interface ISomeService
{
    Guid Id { get; }
}

public class SomeService : ISomeService
{
    public Guid Id { get; }

    public SomeService()
    {
        Id = Guid.NewGuid();
    }

    public SomeService(Guid id)
    {
        Id = id;
    }
}

// Startup.cs:
builder.RegisterType<SomeService>().As<ISomeService>().InstancePerLifetimeScope();

// TestController.cs:
[Route("api/[controller]")]
public class TestController : Controller
{
    private readonly IComponentContext _context;

    public TestController(IComponentContext context)
    {
        _context = context;
    }

    [HttpGet]
    public IActionResult Get()
    {
        var service = _context.Resolve<ISomeService>();
        return Ok(service.Id);
    }

    [HttpGet("{id}")]
    public IActionResult Get(Guid id)
    {
        var service = _context.Resolve<ISomeService>(new NamedParameter("id", id));
        return Ok(service.Id);
    }
}

// GET http://localhost:5000/api/test/e0198f72-6337-4880-b608-68935122cdea
// each and every response will be the same: e0198f72-6337-4880-b608-68935122cdea

// GET http://localhost:5000/api/test
// this way it responds with some random guid each time endpoint is called
...