注册两个继承相同接口的组件

本文关键字:接口 组件 继承 两个 注册 | 更新日期: 2023-09-27 17:53:08

我是autoface的新手,我正在努力理解这个概念。基本上就是浏览他们网站上的样本。我正在玩一些代码,我不理解以下内容。

如果我注册两个类似的组件,如todaywriter昨天的作家解析我的容器,它只写出最后注册的一个,所以在这种情况下,它只会写出今天的日期,而忽略昨天的日期。到底发生了什么?我不能注册从同一接口继承的2个组件?如果可以的话,我要怎么把它们都展示出来呢?

class Program
{
    private static IContainer Container { get; set; }
    static void Main(string[] args)
    {
        var builder = new ContainerBuilder();
        builder.RegisterType<ConsoleOutput>().As<IOutput>();
        builder.RegisterType<yesterdayWriter>().As<IDateWriter>();
        builder.RegisterType<TodayWriter>().As<IDateWriter>();
        Container = builder.Build();
        WriteDate();
    }
    public static void WriteDate()
    {
        // Create the scope, resolve your IDateWriter,
        // use it, then dispose of the scope.
        using (var scope = Container.BeginLifetimeScope())
        {
            var writer = scope.Resolve<IDateWriter>();
            writer.WriteDate();
            Console.ReadLine();
        }
    }
}

// This implementation of the IOutput interface
// is actually how we write to the Console. Technically
// we could also implement IOutput to write to Debug
// or Trace... or anywhere else.
public class ConsoleOutput : IOutput
{
    public void Write(string content)
    {
        Console.WriteLine(content);
    }
}
// This interface helps decouple the concept of
// "writing output" from the Console class. We
// don't really "care" how the Write operation
// happens, just that we can write.
public interface IOutput
{
    void Write(string content);
}
// This interface decouples the notion of writing
// a date from the actual mechanism that performs
// the writing. Like with IOutput, the process
// is abstracted behind an interface.
public interface IDateWriter
{
    void WriteDate();
}
// This TodayWriter is where it all comes together.
// Notice it takes a constructor parameter of type
// IOutput - that lets the writer write to anywhere
// based on the implementation. Further, it implements
// WriteDate such that today's date is written out;
// you could have one that writes in a different format
// or a different date.
public class TodayWriter : IDateWriter
{
    private IOutput _output;
    public TodayWriter(IOutput output)
    {
        this._output = output;
    }
    public void WriteDate()
    {
        this._output.Write(DateTime.Today.ToShortDateString());
    }
}
// This TodayWriter is where it all comes together.
// Notice it takes a constructor parameter of type
// IOutput - that lets the writer write to anywhere
// based on the implementation. Further, it implements
// WriteDate such that today's date is written out;
// you could have one that writes in a different format
// or a different date.
public class yesterdayWriter : IDateWriter
{
    private IOutput _output;
    public yesterdayWriter(IOutput output)
    {
        this._output = output;
    }
    public void WriteDate()
    {
        this._output.Write(DateTime.Today.AddDays(-1).ToShortDateString());
    }
}

注册两个继承相同接口的组件

在您的代码示例中,您的容器包含2个与IDateWriter服务匹配的注册。当您解析IDateWriter服务时,Autofac会给您最新的注册,在您的示例中是TodayWriter

如果你想解决所有的IDateWriter,你可以解决IEnumerable<IDateWriter>

foreach (var writer in scope.Resolve<IEnumerable<IDateWriter>>())
{
    writer.WriteDate();
}

如果您想更进一步,您可能需要一个聚合的IDateWriter

例如:

public class AggregatedDateWriter : IDateWriter
{
    public AggregatedDateWriter(IEnumerable<IDateWriter> writers)
    {
        this._writers = writers; 
    }

    private readonly IEnumerable<IDateWriter> _writers;

    public void WriteDate()
    {
        foreach (IDateWriter writer in this._writers)
        {
            writer.WriteDate();
        }
    }
}

如果您尝试注册此类型,您将看到以下错误消息

Autofac.Core。DependencyResolutionException:检测到循环组件依赖:ConsoleApplication75。AggregatedDateWriter

这是正常的,因为当AggregatedDateWriter被激活时,您试图解析所有IDateWriter

为了避免这种情况,你可以改变组件注册的方式。

builder.RegisterType<yesterdayWriter>()
        .Named<IDateWriter>("concrete");
builder.RegisterType<TodayWriter>()
        .Named<IDateWriter>("concrete");
builder.RegisterType<AggregatedDateWriter>()
        .As<IDateWriter>()
        .WithParameter((pi, c) => pi.Name == "writers", 
                        (pi, c) => c.ResolveNamed<IEnumerable<IDateWriter>>("concrete"));

WithParameter方法告诉Autofac它应该如何处理组件的参数。如果你的AggregatedDateWriter构造函数有一个类型为Stringvalue参数。您可以使用.WithParameter("value", "anyString")方法让Autofac在构造函数中使用此值。在本例中,第一个参数将查找参数名称writers,第二个参数将告诉Autofac使用c.ResolveNamed<IEnumerable<IDateWriter>>("concrete")的结果作为其值。

如果您还想更进一步,您可以使用Castle.Core生成一个代理,它将自动为您生成AggregatedDateWriter,并使用自定义IRegistrationSource注册该代理。