将特定程序集类型注册为已实现的接口时的行为

时间:2015-08-30 11:21:20

标签: c# autofac

Autofac测试。

1个名为IEchoRepo的界面及其2个已实施的类EchoRepoTestEchoRepo

我只是想知道为什么Autofac构建器会选择非测试构建器(如果是EchoRepo),如果我按以下方式放置构建器:

builder.RegisterAssemblyTypes(AppDomain.CurrentDomain.GetAssemblies())
       .Where(t => t.IsAbstract == false && t.Name.EndsWith("Repo"))
       //.Except <EchoRepo>() <-- don't except any one
       .AsImplementedInterfaces()
       .InstancePerRequest();     

==&GT;结果:EchoRepo

这意味着如果我想使用我的TestEchoRepo,我需要排除非测试类,如下所示:

// Test Mode
builder.RegisterAssemblyTypes(AppDomain.CurrentDomain.GetAssemblies())
       .Where(t => t.IsAbstract == false && t.Name.EndsWith("Repo"))
       .Except <EchoRepo>() // exclude what I don't want
       .AsImplementedInterfaces()
       .InstancePerRequest();

==&GT;结果:TestEchoRepo

另一种方式,我也可以通过这个配置。但是,这并不是我所要求的。

builder.RegisterType<TestEchoRepo>()
       .As<IEchoRepo>()
       .InstancePerRequest();

IEchoRepo.cs / EchoRepo.cs / TestEchoRepo.cs

public interface IEchoRepo : IBaseRepository
{
    string Say();
}

public class EchoRepo : IEchoRepo
{
    public string Say()
    {
        return "EchoRepo";
    }
}

public class TestEchoRepo : IEchoRepo
{
    public string Say()
    {
        return "TestEchoRepo";
    }
}

因此,我可以说Autofac构建器将通过一些命名规则选择实现的接口吗?

不太清楚。任何提示都将不胜感激。感谢。

2 个答案:

答案 0 :(得分:1)

如果您查看the source of RegisterAssemblyTypes,您将看到的是通过您传入的程序集集上的LINQ查询检索的类型。

当你把它煮沸时,在伪代码中,它相当于:

public void RegisterAssemblies(Assembly[] assemblies)
{
  foreach(var assembly in assemblies)
  {
    foreach(var type in assembly.GetTypes())
    {
      Register(type);
    }
  }
}

同样,这不是实际代码(显然),而是基本上在异常处理之后归结为什么,过滤掉无法加载的类型,过滤掉无法实例化的类型(委托,接口),以及等等。

然后,注册顺序受到您在装配中输入的顺序以及反射API提供每个装配中类型列表的顺序的影响。

基本上,Autofac并非“选择”类型注册的订单 - .NET反射。

Autofac一般的工作方式是,当您注册多个服务(EchoRepoTestEchoRepo最后一个获胜时。如果你得到的是TestEchoRepo因为在汇编/类型解析列表中,要么首先为程序集提供EchoRepo ,要么它们都在在同一个程序集中,反射在第一个类型列表中提供EchoRepoTestEchoRepo稍后会被注册,因此当您解决一个IEchoRepo时,您获得的是最后注册的一个TestEchoRepo

如果您需要控制类型和注册的顺序,建议您手动注册类型而不是进行大规模的装配扫描。

答案 1 :(得分:0)

虽然我要注意不要在所有程序集中注册所有类型 - 因为你实际上放弃了很多控制并让自己暴露在一连串的运行时错误中。您可以使用标记界面或自定义属性解决您的问题。

public interface IMock { }

public class TestEchoRepo : IEchoRepo, IMock
{
    public string Say()
    {
        return "TestEchoRepo";
    }
}

// Test environment
builder.RegisterAssemblyTypes(AppDomain.CurrentDomain.GetAssemblies())
            .Where(t => t.IsAbstract == false && t.Name.EndsWith("Repo"))
            .Where(t => typeof(IMock).IsAssignableFrom(t))
            .AsImplementedInterfaces()
            .InstancePerRequest();

为了更加通用,您可能希望这样做:

public interface IMock<T> where T : class { } 

public class TestEchoRepo: IEchoRepo, IMock<IEchoRepo>
{
   ...
}

// Test environment
builder.RegisterAssemblyTypes(AppDomain.CurrentDomain.GetAssemblies())
            .Where(t => t.IsAbstract == false)
            .Where(t => t.IsClosedTypeof(typeof(IMock<>)))
            .AsImplementedInterfaces()
            .InstancePerRequest();