基于请求参数动态解析Autofac依赖(ASP.NET Web API场景)
要实现根据请求里的IdLoja参数动态选择对应DLL中的IArtigoErp实现,我们需要调整Autofac的注册方式,同时解决请求参数的传递与动态解析的问题。下面是分步解决方案:
1. 修改Autofac模块,给每个实现添加命名标识
当前你的ExModule只是把所有IArtigoErp实现注册为同一服务,Autofac默认会覆盖,最终只会保留最后一个注册的实现。我们需要给每个DLL的实现加上命名标识,这样就能区分不同来源的服务:
public class ExModule : Autofac.Module { protected override void Load(ContainerBuilder builder) { // 注册时指定命名,DLL1的实现命名为"DLL1" RegistaDepedencias<IArtigoErp>(builder, "DLL1"); // DLL2的实现命名为"DLL2" RegistaDepedencias<IArtigoErp>(builder, "DLL2"); } private void RegistaDepedencias<T>(ContainerBuilder builder, string NomeDll) { RegisterDep<T>(GetEnumerableTypes<T>(NomeDll), builder, NomeDll); } // 新增NomeDll参数,用于给服务命名 private void RegisterDep<T>(IEnumerable<Type> types, ContainerBuilder builder, string serviceName) { foreach (var t in types) { // 使用Named方法给每个实现指定唯一名称 builder.RegisterType(t).Named<T>(serviceName); } } private IEnumerable<Type> GetEnumerableTypes<T>(string NomeDll) { return Directory.EnumerateFiles(Path.Combine(HostingEnvironment.ApplicationPhysicalPath, "Engine")) .Where(x => x.Contains(NomeDll) && x.EndsWith(NomeDll +".dll")) .Select(x => Assembly.LoadFrom(x)) .SelectMany(x => x.GetTypes() .Where(t => typeof(T).IsAssignableFrom(t) && t.IsClass)); } }
2. 处理请求参数:通过ActionFilter存储DLL映射关系
因为IdLoja是Post请求体里的参数,直接在BeginRequest里读取请求体会导致后续控制器无法再读取(请求体流只能读取一次),所以我们用ActionFilter来在Action执行前获取参数,并将对应的DLL名称存储到请求上下文中:
首先创建一个Filter:
public class StoreDllMappingFilter : ActionFilterAttribute { // 建议把映射关系放到配置文件,这里先示例硬编码 private static readonly Dictionary<Guid, string> _lojaToDllMap = new Dictionary<Guid, string> { { Guid.Parse("你的第一个门店Guid"), "DLL1" }, { Guid.Parse("你的第二个门店Guid"), "DLL2" } }; public override void OnActionExecuting(HttpActionContext actionContext) { // 从Action参数中获取IdLoja if (actionContext.ActionArguments.TryGetValue("IdLoja", out var idObj) && idObj is Guid idLoja && _lojaToDllMap.TryGetValue(idLoja, out var dllName)) { // 将选中的DLL名称存储到请求属性中 actionContext.Request.Properties["SelectedDllName"] = dllName; } else { // 处理无效IdLoja的情况,比如返回错误或抛出异常 actionContext.Response = actionContext.Request.CreateErrorResponse( HttpStatusCode.BadRequest, "无效的门店ID"); } base.OnActionExecuting(actionContext); } }
3. 动态解析对应服务:两种可选方案
方案一:在控制器中使用IIndex<string, IArtigoErp>直接解析
Autofac提供了IIndex<TKey, TValue>接口,可以通过名称直接获取对应的命名服务。修改控制器如下:
public class ArtigoController : ApiController { // 注入IIndex而不是直接注入IArtigoErp private readonly IIndex<string, IArtigoErp> _artigoErpIndex; private readonly IArtigoLoja _artigoLoja; public ArtigoController(IIndex<string, IArtigoErp> artigoErpIndex, IArtigoLoja artigoLoja) { _artigoErpIndex = artigoErpIndex; _artigoLoja = artigoLoja; } [Route("PostArtigos")] [StoreDllMappingFilter] // 应用Filter public CallResponse PostArtigos([FromBody] Guid IdLoja) { // 从请求属性中获取选中的DLL名称 if (Request.Properties.TryGetValue("SelectedDllName", out var dllObj) && dllObj is string dllName) { // 根据名称获取对应的IArtigoErp实现 var artigoErp = _artigoErpIndex[dllName]; // 调用对应DLL的方法 var lista = artigoErp.GetLista(); // 后续业务逻辑... return new CallResponse { /* 填充响应数据 */ }; } return new CallResponse { /* 错误响应 */ }; } }
方案二:通过委托注册自动解析(保持控制器原有注入方式)
如果你想保持控制器的构造函数不变,还是直接注入IArtigoErp,可以通过Autofac的委托注册,让它自动根据请求上下文选择对应的实现:
首先,在global.asax的容器构建代码中注册IHttpContextAccessor:
var containerBuilder = new ContainerBuilder(); containerBuilder.RegisterApiControllers(Assembly.GetExecutingAssembly()); // 注册HttpContext访问器 containerBuilder.RegisterType<HttpContextAccessor>().As<IHttpContextAccessor>(); containerBuilder.RegisterModule<ExModule>(); // 委托注册IArtigoErp,根据请求上下文选择对应的命名服务 containerBuilder.Register(c => { var httpContext = c.Resolve<IHttpContextAccessor>().HttpContext; if (httpContext?.Items["SelectedDllName"] is string dllName) { return c.ResolveNamed<IArtigoErp>(dllName); } throw new InvalidOperationException("无法确定要使用的DLL"); }).As<IArtigoErp>().InstancePerRequest(); var container = containerBuilder.Build(); container.Resolve<IArtigoErp>();
然后控制器就可以保持原有代码,只需要添加Filter:
public class ArtigoController : ApiController { private readonly IArtigoErp _artigoErp; private readonly IArtigoLoja _artigoLoja; public ArtigoController(IArtigoErp artigoErp, IArtigoLoja artigoLoja) { _artigoErp = artigoErp; _artigoLoja = artigoLoja; } [Route("PostArtigos")] [StoreDllMappingFilter] // 应用Filter public CallResponse PostArtigos([FromBody] Guid IdLoja) { // 直接使用_artigoErp,它已经是根据IdLoja选择的对应DLL实现 var lista = _artigoErp.GetLista(); // 后续业务逻辑... return new CallResponse { /* 填充响应数据 */ }; } }
注意事项
- 映射配置优化:不要硬编码
IdLoja和DLL的映射关系,建议放到web.config或者配置文件中,比如用appSettings存储,然后在Filter中读取解析,方便后续修改。 - 异常处理:要处理
IdLoja无对应DLL的情况,避免空引用或未知错误。 - 请求体读取问题:绝对不要在
BeginRequest中直接读取Post请求体,否则控制器会无法获取参数,这也是我们选择ActionFilter的原因。
内容的提问来源于stack exchange,提问作者Bruno Adão
相关产品推荐
相关产品推荐

