如何用Autofac自动注册泛型仓储的所有子类型?
如何自动注册Autofac仓储类型,避免手动维护注册列表
我目前在使用Autofac时,有大量重复的仓储注册代码:
builder.RegisterType<ZipCodeRepository<ZipCode>>().AsImplementedInterfaces(); builder.RegisterType<MemberRepository<Member>>().AsImplementedInterfaces(); builder.RegisterType<CreditRepository<Credit>>().AsImplementedInterfaces(); builder.RegisterType<DebitRepository<Debit>>().AsImplementedInterfaces(); builder.RegisterType<StateRepository<State>>().AsImplementedInterfaces(); builder.RegisterType<CityRepository<City>>().AsImplementedInterfaces();
我想把这些代码简化成自动注册的形式,类似这样:
Type[] inheritedTypes; Type baseType; baseType = typeof(Repository<Entity>); inheritedTypes = baseType.Assembly.GetTypes().Where(type => { return type.BaseType.Name == baseType.Name; }).ToArray(); builder.RegisterTypes(inheritedTypes).AsImplementedInterfaces();
LINQ查询能正确返回所有继承自Repository<Entity>的仓储类型,但Autofac解析时抛出错误:
Cannot resolve parameter
IZipCodeRepository<ZipCode> ZipCodeRepository
我在如下处理器类中尝试解析仓储:
public class GetZipCodeByCodeQueryHandler : Handler { public GetZipCodeByCodeQueryHandler(string unitOfWork, IZipCodeRepository<ZipCode> zipCodeRepository) : base(unitOfWork) { _zipCodeRepository = zipCodeRepository; } }
我的目标是自动注册所有仓储,不用为每个新实体手动添加注册项。我试过两种方案,但都无效:
第一种方案:
builder .RegisterAssemblyTypes(baseType.Assembly) .Where(type => type.IsClosedTypeOf(typeof(Repository<>))) .As(type => GetIRepositoryType(type)).AsImplementedInterfaces();
我分别尝试了Repository<>和IRepository<>,结果都一样,还是报错。
补充说明
我发现一段奇怪的现象:下面代码中的t最终是空列表,看起来Autofac根本没有遍历任何类型:
var t = new List<Type>(); builder .RegisterAssemblyTypes(AppDomain.CurrentDomain.GetAssemblies()) .Where(type => { t.Add(type); return type.IsClosedTypeOf(typeof(Repository<>)); }) .As(type => this.GetIRepositoryType(type)).AsImplementedInterfaces();
以下是完整的可复现代码:
using System; using System.Linq; using Autofac; public static class Program { public static void Main(string[] Args) { Succeeds(); Fails(); } private static void Succeeds() { IContainer container; ContainerBuilder builder; builder = new ContainerBuilder(); builder.RegisterType<ZipCodeRepository<ZipCode>>().AsImplementedInterfaces(); container = builder.Build(); container.Resolve<IZipCodeRepository<ZipCode>>(); } private static void Fails() { Type baseType; Type[] allTypes; IContainer container; ContainerBuilder builder; baseType = typeof(Repository<Entity>); allTypes = baseType.Assembly.GetTypes().ToArray(); Console.WriteLine(allTypes.Contains(typeof(ZipCodeRepository<ZipCode>))); builder = new ContainerBuilder(); builder.RegisterTypes(allTypes).AsImplementedInterfaces(); container = builder.Build(); container.Resolve<IZipCodeRepository<ZipCode>>(); } } public interface IRepository<T> where T : Entity { } public abstract class Repository<T> : IRepository<T> where T : Entity { } public abstract class Entity { public int Id { get; set; } } public interface IZipCodeRepository<T> : IRepository<ZipCode> where T : ZipCode { } public class ZipCodeRepository<T> : Repository<ZipCode>, IZipCodeRepository<ZipCode> where T : ZipCode { } public class ZipCode : Entity { }
请问如何用单个表达式实现仓储的自动注册,无需手动维护注册列表?
解决方案
问题根源分析
- 你的
ZipCodeRepository<T>定义存在冗余:泛型参数T未被实际使用,导致它被识别为开放泛型类型,但实际继承的是闭合泛型的Repository<ZipCode>,Autofac在匹配接口时会出现混淆。 - 最初的筛选逻辑
type.BaseType.Name == baseType.Name仅通过类型名字匹配,没有考虑泛型类型的实际参数,逻辑不够严谨。 IsClosedTypeOf(typeof(Repository<>))检查失败,因为ZipCodeRepository<T>本身是开放泛型,而非Repository<>的闭合版本。
正确的自动注册代码
针对你的场景,使用以下代码可以实现正确的自动注册:
var builder = new ContainerBuilder(); var assembly = typeof(Repository<Entity>).Assembly; // 筛选所有非抽象、继承自Repository<T>(任意实体T)的仓储类 builder.RegisterAssemblyTypes(assembly) .Where(t => !t.IsAbstract && t.BaseType != null && t.BaseType.IsGenericType && t.BaseType.GetGenericTypeDefinition() == typeof(Repository<>)) .AsImplementedInterfaces(); var container = builder.Build(); // 现在可正常解析IZipCodeRepository<ZipCode> var repo = container.Resolve<IZipCodeRepository<ZipCode>>();
优化建议
如果你的仓储类遵循[实体名]Repository实现I[实体名]Repository<[实体名]>的命名规范,可以进一步优化注册逻辑,确保精准匹配:
builder.RegisterAssemblyTypes(assembly) .Where(t => !t.IsAbstract && t.Name.EndsWith("Repository")) .As(t => t.GetInterfaces().FirstOrDefault(i => i.Name == $"I{t.Name}"));
同时建议修改ZipCodeRepository<T>的定义,移除未使用的泛型参数,简化为:
public class ZipCodeRepository : Repository<ZipCode>, IZipCodeRepository<ZipCode> { }
内容的提问来源于stack exchange,提问作者InteXX
相关产品推荐
相关产品推荐

