如何在Autofac注册阶段解析依赖?原Unity用户的迁移咨询
从Unity IOC迁移到Autofac:多实现接口的注册与工厂模式解决方案
好的,我来帮你搞定这个Unity转Autofac的问题——我之前也处理过类似的多实现接口注册+工厂注入的场景,Autofac的思路和Unity确实不一样,核心是不能在注册阶段直接Resolve,但我们可以用它的键控注册和上下文解析来实现相同的效果。
第一步:用键控注册替代Unity的TypedParameter
在Unity里你通过TypedParameter来区分不同的IQueryManager实现,Autofac里更合适的方式是用Keyed注册,直接把DatabaseType枚举作为键,这样后续可以精准匹配:
// 给每个IQueryManager实现绑定对应的DatabaseType键 builder.RegisterType<FirstManager>() .Keyed<IQueryManager>(GeneralEnumerations.DatabaseType.First); builder.RegisterType<SecondManager>() .Keyed<IQueryManager>(GeneralEnumerations.DatabaseType.Two); builder.RegisterType<ThirdManager>() .Keyed<IQueryManager>(GeneralEnumerations.DatabaseType.Three);
这种方式比命名注册更贴合你的枚举类型,避免字符串硬编码的问题。
第二步:注册工厂并注入解析逻辑
你之前的写法失效主要有两个原因:一是没给实现做标识,Autofac不知道该解析哪个;二是在注册阶段直接调用Resolve,而Autofac要求容器Build后才能解析。
正确的做法是利用Autofac的IComponentContext上下文,把解析逻辑封装成Func,在工厂实际被使用的时候才去解析,而不是注册阶段。这里有两种简洁的写法:
写法一:直接给工厂注入Func参数
// 先注册一个Func<DatabaseType, IQueryManager>,用上下文来根据键解析 builder.Register<Func<GeneralEnumerations.DatabaseType, IQueryManager>>(ctx => { var componentContext = ctx.Resolve<IComponentContext>(); return dbType => componentContext.ResolveKeyed<IQueryManager>(dbType); }); // 然后正常注册工厂,Autofac会自动把上面的Func注入到工厂的构造函数里 builder.RegisterType<QueryManagerFactory<IQueryManager>>() .As<IQueryManagerFactory<IQueryManager>>();
写法二:用ResolvedParameter手动注入
如果你的工厂构造函数需要更精准的参数匹配,可以用这种方式:
builder.RegisterType<QueryManagerFactory<IQueryManager>>() .As<IQueryManagerFactory<IQueryManager>>() .WithParameter( // 匹配构造函数中的Func参数 new ResolvedParameter( (paramInfo, ctx) => paramInfo.ParameterType == typeof(Func<GeneralEnumerations.DatabaseType, IQueryManager>), (paramInfo, ctx) => new Func<GeneralEnumerations.DatabaseType, IQueryManager>(dbType => ctx.ResolveKeyed<IQueryManager>(dbType) ) ) );
两种方式本质是一样的,都是让Autofac在工厂被实例化时,注入一个能根据DatabaseType获取对应IQueryManager的Func。
第三步:验证使用
当容器Build完成后,你就可以像Unity里那样使用工厂了:
using (var container = builder.Build()) { var queryFactory = container.Resolve<IQueryManagerFactory<IQueryManager>>(); var firstDbManager = queryFactory.GetQueryManager(GeneralEnumerations.DatabaseType.First); // 正常使用firstDbManager... }
为什么你之前的写法不生效?
- 缺少实现标识:你没有给每个
IQueryManager的实现做命名或键控标记,Autofac无法区分多个实现,默认只会返回最后注册的那个; - 注册阶段Resolve错误:Autofac的
ContainerBuilder本身没有Resolve方法,注册时的container参数是IComponentContext,但此时容器还没Build,直接调用Resolve会引发异常或者无法正确解析。
内容的提问来源于stack exchange,提问作者lilan silva




