You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何通过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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.28 07:25:50