繁体   English   中英

Autofac-通过控制器构造函数参数解析通用类型

[英]Autofac - resolve Generic Type through Controller constructor parameter

我有抽象的泛型类

 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()-向我返回我需要的实例的名称(默认值特定)。

否,我想将服务注入控制器以使其能够加载所需的逻辑服务。

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

}

编译器要求我定义模型而不是???。 但是,我不需要它,因为Implementation ob抽象类已经选择了继承而不是继承的开放泛型类型。 还有其他方法可以通过autofac解决所需的逻辑吗?

我认为您在这里有或多或少的三个选择。

1)使控制器通用,然后将控制器的type参数传递给注入的类型本身(请参见下文)。 这样,控制器将知道特定的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());
    }
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM