Autofac - разрешить общий тип через параметр конструктора контроллера - PullRequest
0 голосов
/ 21 мая 2018

У меня есть абстрактный обобщенный класс

 public abstract class AbstractLogic<T> {}

и две реализации

DefaultLogic  : AbstractLogic<ClassA>{}
SpecificLogic : AbstractLogic<ClassB>{}

Как мы видим, если я хочу создать экземпляр одного из них, я могу легко сделать это без указанияОткройте универсальный класс.

DefaultLogic logic= new DefaultLogic();

Теперь мне нужно создать экземпляр каждого из них с помощью DI.Итак, я зарегистрировал такие типы

    var logicList= new[]
        {
            Assembly.GetAssembly(typeof(DefaultLogic)),
            Assembly.GetAssembly(typeof(SpecificLogic))
        };
        builder.RegisterAssemblyTypes(logicList).AsClosedTypesOf(typeof(AbstractLogic<>))
            .Where(t => t.Name.StartsWith(Settings.GetCurrentMode().ToString()));

Settings.GetCurrentMode () - возвращает мне имя нужного мне экземпляра (Default of Specific).

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

public class ListController : Controller
{
    private AbstractLogic<???> _logic;
    public ListController(AbstractLogic<???> logic)
        {
            _logic = logic;
        }

}

Компилятор просит меня определить модель вместо ???.Но мне это не нужно, так как реализация ob абстрактного класса уже выбрала открытый универсальный тип вместо наследования.Есть ли другой способ, как я могу решить необходимую логику с помощью автофака?

1 Ответ

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

По моему мнению, у вас есть три более или менее реальных варианта.

1) Сделайте контроллер универсальным, а затем передайте параметр типа контроллера самому введенному типу (см. Ниже).Таким образом, контроллер будет знать о конкретном типе T и сможет использовать его без каких-либо странных танцев.Однако следующий вопрос - как объяснить все эти вещи в рамках, которые будут разрешать такой контроллер?DotNet Core по умолчанию не поддерживает универсальные контроллеры.К счастью, такой трюк вполне можно сделать.Вы можете прочитать некоторые подробности об этом в этом ответе и далее по ссылке к документации Microsoft .Это не просто, так что это может быть излишним для вашей задачи ... Я просто не уверен, что ваша задача на самом деле.

[Route("api/[controller]")]
public class ValuesController<T> : Controller where T: class, new()
{
    private readonly AbstractLogic<T> _logic;

    public ValuesController(AbstractLogic<T> logic)
    {
        _logic = logic;
    }

2) Используйте отражение, чтобы разрешить AbstractLogic<T>.Прежде всего, вам нужно объявить приватное поле для хранения ссылки на AbstractLogic<T>.Единственный способ, как это можно сделать, описан здесь .После этого вам нужно разрешить его, используя определенный тип , и единственный способ сделать это - создать этот тип во время выполнения с помощью отражения.Такие вещи имеют тенденцию становиться довольно громоздкими довольно быстро.

3) Самый разумный вариант, на мой взгляд.Вам необходимо ввести базовый неуниверсальный тип для AbstractLogic, наследовать от него AbstractLogic<T>, зарегистрировать конкретную универсальную реализацию в контейнере как неуниверсальную AbstractLogic, а затем просто внедрить неуниверсальный AbstractLogic в свой контроллер.Таким образом, вам нужно переместить универсальную логику в общие классы, в то же время перенеся интерфейс существенного компонента в неуниверсальный родительский AbstractLogic.Однако это добавляет ограничение: основной компонент неуниверсального интерфейса никоим образом не должен зависеть от конкретного параметра универсального типа дочернего класса.Поскольку я понятия не имею об остальной части вашего кода, я не могу сказать, возможно ли это в вашем случае или нет.Общая идея кода приведена ниже.

// class hierarchy
public abstract class AbstractLogic
{
    public string DoStufF()
    {
        return DoStufFInternal();
    }

    protected abstract string DoStufFInternal();
}

// Here 'where' constraint is not essential, I'm just lazy enough
// and implemented property setting in the dumbest possible way which required the constraint :)
public abstract class AbstractLogic<T> : AbstractLogic where T: class, new()
{
    protected AbstractLogic()
    {
        SomeProperty = new T();
    }

    public T SomeProperty { get; private set; }
}

public class DefaultLogic : AbstractLogic<ClassA>
{
    protected override string DoStufFInternal()
    {
        return $"Default stuff: SomeProperty = {SomeProperty.ToString()}";
    }
}

public class SpecificLogic : AbstractLogic<ClassB>
{
    protected override string DoStufFInternal()
    {
        return $"Specific stuff: SomeProperty = {SomeProperty.ToString()}";
    }
}

public class ClassA
{
    public override string ToString()
    {
        return "Class A representation";
    }
}

public class ClassB
{
    public override string ToString()
    {
        return "Class B representation";
    }
}

// registering class
builder.RegisterAssemblyTypes(Assembly.GetExecutingAssembly())
    .Where(t => t.Name.StartsWith(Settings.GetCurrentMode().ToString()))
    .As<AbstractLogic>()
    .InstancePerLifetimeScope();

// and using it in the controller
[Route("api/[controller]")]
public class DITestController : Controller
{
    private readonly AbstractLogic _logic;

    public DITestController(AbstractLogic logic)
    {
        _logic = logic;
    }

    [HttpGet]
    public IActionResult Get()
    {
        return Ok(_logic.DoStufF());
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...