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

如何用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
{
}

请问如何用单个表达式实现仓储的自动注册,无需手动维护注册列表?


解决方案

问题根源分析

  1. 你的ZipCodeRepository<T>定义存在冗余:泛型参数T未被实际使用,导致它被识别为开放泛型类型,但实际继承的是闭合泛型的Repository<ZipCode>,Autofac在匹配接口时会出现混淆。
  2. 最初的筛选逻辑type.BaseType.Name == baseType.Name仅通过类型名字匹配,没有考虑泛型类型的实际参数,逻辑不够严谨。
  3. 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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.08 11:00:32