使用 autofac 注入两个相同类型的对象

Inject two objects of same type using autofac

我一直在尝试使用autofac注册并注入两个相同类型的不同对象,但我无法成功注入第二个对象。注入的第二个对象始终是使用 SingleInstance() 时首先注册的类型 1 的同一实例,或者是使用 InstancePerLifetimeScope 时类型 1 的另一个实例. InstancePerDependency 也不起作用。关于如何实现这一点有什么建议吗?谢谢!

找到一个类似的未解决问题 - Inject multiple instances of same type - Autofac

    /* Register named object of type1 */
    builder.RegisterType<TestClient>()
             .As<TestClient>()
             .UsingConstructor(typeof(Settings), typeof(SampleEnum))
             .WithParameters(new[]{
                new ResolvedParameter((p, c) => p.ParameterType.IsAssignableTo<Settings>(), (p, c) => c.Resolve<Settings>()),
                new ResolvedParameter((p, c) => p.ParameterType == typeof(SampleEnum) && p.Name == "eventtype",(p, c) => SampleEnum.Type1),
                }).Named<TestClient>(clientoftype1).InstancePerDependency();

    /* Register named object of type2 */
    builder.RegisterType<TestClient>()
              .As<TestClient>()
             .UsingConstructor(typeof(Settings), typeof(SampleEnum))
             .WithParameters(new[]{
                new ResolvedParameter((p, c) => p.ParameterType.IsAssignableTo<Settings>(), (p, c) => c.Resolve<Settings>()),
                new ResolvedParameter((p, c) => p.ParameterType == typeof(SampleEnum) && p.Name == "eventtype",(p, c) => SampleEnum.Type2),
                }).Named<TestClient>(clientoftype2)).InstancePerDependency();

    /*Controller registration
        public DevController(TestClient clientoftype1, TestClient clientoftype2)
        {..}
     */         
    builder
    .RegisterType<DevController>()
    .As<DevController>()
    .WithParameters(new []{
        ResolvedParameter.ForNamed<TestClient>("clientoftype1"),
        ResolvedParameter.ForNamed<TestClient>("clientoftype2"),
    } ).InstancePerRequest();

   /*Check registered types*/
   var types = container.ComponentRegistry.Registrations
     .Where(r => typeof(TestClient).IsAssignableFrom(r.Activator.LimitType))
     .Select(r => r.Activator.LimitType);
   var countofobjects = types.ToList();//This has 2 objects.

一般来说你在做什么 - 在构造函数中需要多个相同类型但作为单独的参数是排序依赖注入的禁忌。也就是说,这通常是 避免做的事 :

public class Consumer
{
  public Consumer(Dependency a, Dependency b) { /* ... */ }
}

原因是,如您所见,DI 主要用于基于类型的注入。这不是 .NET 或 Autofac 独有的,它就是这样。从设计的角度来看,我可能会问你为什么不做类似的事情...

public class Consumer
{
  public Consumer(IEnumerable<Dependency> dependencies) { /* ... */ }
}

我会这样问,因为如果你不能将这两个依赖关系 视为相同的 这有点违反了 Liskov substitution principle 而你实际上想要有不同的接口(即使它们看起来一样)来区分这两个东西。

public class Consumer
{
  public Consumer(IDependencyA a, IDependencyB b) { /* ... */ }
}

但是,假设您无法重新设计,您可以使用 named services along with the KeyFilterAttribute 来获得您想要的。

这是一个完整的最小控制台应用程序,展示了它的工作原理。

using System;
using Autofac;
using Autofac.Features.AttributeFilters;

namespace AutofacDemo
{
    public static class Program
    {
        public static void Main()
        {
            var builder = new ContainerBuilder();
            builder.RegisterType<Consumer>().WithAttributeFiltering();
            builder.RegisterType<Dependency>().SingleInstance().AsSelf().Named<Dependency>("a");
            builder.RegisterType<Dependency>().SingleInstance().AsSelf().Named<Dependency>("b");
            using var container = builder.Build();

            var consumer = container.Resolve<Consumer>();
            consumer.WriteInfo();
        }
    }

    public class Consumer
    {
        private readonly Dependency _a;
        private readonly Dependency _b;

        public Consumer([KeyFilter("a")]Dependency a, [KeyFilter("b")] Dependency b)
        {
            this._a = a;
            this._b = b;
        }

        public void WriteInfo()
        {
            Console.WriteLine("A: {0}", this._a.Id);
            Console.WriteLine("B: {0}", this._b.Id);
        }
    }

    public class Dependency
    {
        public Dependency()
        {
            this.Id = Guid.NewGuid();
        }

        public Guid Id { get; }
    }
}

当你运行这个时,你会得到两个不同的 ID,比如:

A: 542f8ae9-bd04-4821-a3e5-5eb3c41bbbc6
B: cb9b8245-c12e-4928-b618-0ecbf0a75a84

如果您要删除过滤器属性,那么您将获得 相同的 ID,因为如您所见,按类型注入每次都会以相同的实例结束 -最后获胜。过滤是让它发挥作用的魔法。