我一直在尝试使用 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.
答案 0 :(得分: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
如果您要删除过滤器属性,那么您将获得 相同 ID,因为如您所见,按类型注入每次都会以相同的实例结束 - 最后是胜利。过滤是让它发挥作用的魔法。