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

基于请求参数动态解析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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.08 13:38:13