注册两个继承相同接口的组件
registering 2 components which inherits same interface
我是 autofac 的新手,我正在尝试理解这个概念。所以基本上浏览他们网站上的示例。我正在玩一些代码,但我不明白以下内容。
如果我如下注册两个类似的组件,如 todaywriter 和 yesterdaywriter 并解析我的容器,它只会写出最后注册的一,所以在这种情况下,它只会写出今天的日期而忽略昨天的日期。到底发生了什么?我无法注册从同一接口继承的 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: Circular component dependency detected: 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
构造函数有一个类型为 String
的 value
参数。您可以使用 .WithParameter("value", "anyString")
方法让 Autofac
在构造函数中使用此值。在这种情况下,第一个参数将查找参数名称 writers
,第二个参数将告诉 Autofac
使用 c.ResolveNamed<IEnumerable<IDateWriter>>("concrete")
的结果作为其值。
如果您还想更进一步,您可以使用 Castle.Core
生成代理,它将自动为您生成 AggregatedDateWriter
,并使用自定义 IRegistrationSource
注册此代理。
我是 autofac 的新手,我正在尝试理解这个概念。所以基本上浏览他们网站上的示例。我正在玩一些代码,但我不明白以下内容。
如果我如下注册两个类似的组件,如 todaywriter 和 yesterdaywriter 并解析我的容器,它只会写出最后注册的一,所以在这种情况下,它只会写出今天的日期而忽略昨天的日期。到底发生了什么?我无法注册从同一接口继承的 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: Circular component dependency detected: 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
构造函数有一个类型为 String
的 value
参数。您可以使用 .WithParameter("value", "anyString")
方法让 Autofac
在构造函数中使用此值。在这种情况下,第一个参数将查找参数名称 writers
,第二个参数将告诉 Autofac
使用 c.ResolveNamed<IEnumerable<IDateWriter>>("concrete")
的结果作为其值。
如果您还想更进一步,您可以使用 Castle.Core
生成代理,它将自动为您生成 AggregatedDateWriter
,并使用自定义 IRegistrationSource
注册此代理。