【问题标题】:Inject two objects of same type using autofac使用 autofac 注入两个相同类型的对象
【发布时间】:2021-03-04 17:05:18
【问题描述】:

我一直在尝试使用 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.

【问题讨论】:

  • 您能否详细说明您的期望是什么?我不清楚你的实际目标是什么。为什么需要两次注入相同的类型? minimal reproducible example 来说明问题会很好。
  • @Xerillio - 我想通过注入两个不同类型的 TestClient 对象来注册一个具有如下所示构造函数的控制器。在我的例子中,为这两个参数注入了创建的对象 clientoftype1。 public DevController(TestClient clientoftype1, TestClient clientoftype2)
  • 这似乎是一个奇怪的要求。为什么需要两个相同类型的?也许你应该注入一个工厂并通过它创建两个对象?
  • 感谢@Xerillio 的输入。我正在处理的项目有很多遗留代码,当然需要一些清理。

标签: c# asp.net-mvc autofac autofac-configuration


【解决方案1】:

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

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 和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

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

【讨论】:

  • 谢谢@travis。项目中使用的 autofac 版本(它是遗留代码!)是 4.6.1 似乎不支持 WithAttributeFiltering()。但是,正如您正确指出的那样,这是一个设计问题,最好清理方法。
  • 对于较旧的 Autofac,请查找 Autofac.Extras.AttributeMetadata 包。我认为过滤器在进入核心 Autofac 之前就已经存在。但是,是的,如果你能解决这个问题,那就去做吧。
猜你喜欢
  • 1970-01-01
  • 2023-04-11
  • 1970-01-01
  • 2011-01-30
  • 1970-01-01
  • 2013-05-17
  • 2017-03-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多