如何通过Autofac在Controller构造函数中解析泛型逻辑类实例
我定义了一个抽象泛型类:
public abstract class AbstractLogic<T> {}
还有两个实现类:
public class DefaultLogic : AbstractLogic<ClassA>{} public class SpecificLogic : AbstractLogic<ClassB>{}
直接实例化这些实现类时不用指定泛型类型,比如:
DefaultLogic logic= new DefaultLogic();
现在我通过Autofac做依赖注入,已经这样注册类型了:
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或Specific)。
现在我想在Controller里注入这个服务:
public class ListController : Controller { private AbstractLogic<???> _logic; public ListController(AbstractLogic<???> logic) { _logic = logic; } }
编译器要求我替换???指定具体模型,但实现类已经通过继承确定了泛型类型,不想手动指定。有没有办法让Autofac正确解析所需的逻辑服务?
这里有几个实用的思路,帮你绕开手动指定泛型参数的问题,让Autofac正确解析你需要的服务:
1. 先加一个非泛型的基接口/抽象类
最简单的办法是给你的泛型抽象类套一个非泛型的“壳”——比如定义一个非泛型接口,让AbstractLogic<T>继承它:
public interface ILogic {} public abstract class AbstractLogic<T> : ILogic {}
然后修改Autofac的注册代码,把实现类同时注册为这个非泛型接口:
var logicList = new[] { Assembly.GetAssembly(typeof(DefaultLogic)), Assembly.GetAssembly(typeof(SpecificLogic)) }; builder.RegisterAssemblyTypes(logicList) .AsClosedTypesOf(typeof(AbstractLogic<>)) .As<ILogic>() .Where(t => t.Name.StartsWith(Settings.GetCurrentMode().ToString()));
这样在Controller里就可以直接注入ILogic了,之后如果需要强类型操作,再做类型转换就行:
public class ListController : Controller { private readonly ILogic _logic; public ListController(ILogic logic) { _logic = logic; } public IActionResult Index() { // 比如根据业务判断转成对应的泛型类型 if (_logic is AbstractLogic<ClassA> classALogic) { // 处理ClassA相关逻辑 } else if (_logic is AbstractLogic<ClassB> classBLogic) { // 处理ClassB相关逻辑 } // ... } }
这个方法的好处是快速上手,缺点是需要类型转换,适合不需要频繁调用泛型方法的场景。
2. 用命名服务+工厂模式保持强类型
如果不想做类型转换,想保持强类型的话,可以给注册的服务加个名称,再用工厂来获取实例。
首先修改注册代码,给符合条件的服务加上当前模式作为名称:
var currentMode = Settings.GetCurrentMode().ToString(); var logicList = new[] { Assembly.GetAssembly(typeof(DefaultLogic)), Assembly.GetAssembly(typeof(SpecificLogic)) }; builder.RegisterAssemblyTypes(logicList) .AsClosedTypesOf(typeof(AbstractLogic<>)) .Where(t => t.Name.StartsWith(currentMode)) .Named(currentMode, typeof(AbstractLogic<>));
然后写一个简单的工厂类,用来从Autofac容器里根据名称解析服务:
public class LogicFactory { private readonly IComponentContext _componentContext; public LogicFactory(IComponentContext componentContext) { _componentContext = componentContext; } public AbstractLogic<T> GetTypedLogic<T>() { var mode = Settings.GetCurrentMode().ToString(); return _componentContext.ResolveNamed<AbstractLogic<T>>(mode); } }
接下来在Controller里注入这个工厂,再获取你需要的强类型实例:
public class ListController : Controller { private readonly AbstractLogic<ClassA> _logic; public ListController(LogicFactory factory) { // 根据当前模式获取对应的实例,比如Default模式下就得到DefaultLogic _logic = factory.GetTypedLogic<ClassA>(); } }
这个方法能完美保持强类型,适合需要频繁调用泛型方法的场景。
3. 让Controller也变成泛型(适合特定业务场景)
如果你的ListController本身就是用来处理特定类型的数据,那可以把Controller改成泛型的:
public class ListController<T> : Controller { private readonly AbstractLogic<T> _logic; public ListController(AbstractLogic<T> logic) { _logic = logic; } }
然后在注册路由或者Controller的时候,指定对应的泛型参数(比如ListController<ClassA>),这样Autofac就能自动解析对应的AbstractLogic<T>了。这个方法适合每个数据类型对应一个Controller的场景。
4. 自定义注册源(进阶玩法)
如果上面的方法都不符合你的需求,还可以自定义IRegistrationSource来处理这种动态解析的场景,但这个方法相对复杂,一般只有在特殊需求下才会用到,比如你需要根据请求上下文动态切换泛型类型的情况。
内容的提问来源于stack exchange,提问作者Misha Sloushch

