使用 属性 使用 Autofac 创建委托工厂
Creating a Delegating Factory with Autofac using a property
我正在尝试创建一个工厂来帮助将基于接口 (IIncomingMessage) 的 class 转换为其他基于 classes (AMessage, BMessage) 的新实例单曲class的18=],喜欢:
public interface IIncomingMessage
{
public DeviceTypeEnum DeviceType {get;}
}
public class IncomingMessage : IIncomingMessage
{
public DeviceTypeEnum DeviceType {get {return DeviceTypeEnum.TypeA;}}
Public Byte[] RawData {get; set;}
}
public interface IMessageTransformer<out T> where T:class
{
T Transform(IIncomingMessage message);
}
public class AMessage
{
public int ChoppedUp1 {get; set;}
public int ChoppedUp2 {get; set;}
public int ChoppedUp3 {get; set;}
}
public class BMessage
{
public string SomeData {get; set;}
public string SomeMoreData {get; set;}
}
public class AMessageTransformer : IMessageTransformer<AMessage>
{
public AMessage Transform(IIncomingMessage message)
{
var result = new AMessage();
result.ChoppedUp1 = message.RawData[1];
result.ChoppedUp2 = message.RawData[2];
....
return result;
}
}
public class BMessageTransformation : IMessageTransformer<BMessage>
{
public BMessage Transform(IIncomingMessage message)
{
throw new NotImplementedException();
}
}
public class MessageTransformFactory<T> where T : class
{
private readonly Func<DeviceTypeEnum, IMessageTransformer<T>> _create;
public MessageTransformFactory(Func<DeviceTypeEnum, IMessageTransformer<T>> create)
{
_create = create;
}
public IMessageTransformer<T> GetTransformer(DeviceTypeEnum device)
{
return _create(device);
}
}
我正在使用 Autofac,我很确定应该有一种方法可以创建一个工厂,它可以根据 DeviceType 的 属性 值返回正确的变压器。使用并看到了大量使用对象类型而不是值来做出决定的示例。 Named and Keyed Services 看起来很有前途,但看起来我仍然在某处得到一个静态列表。
根据您提供的示例,它看起来像:
public IMessageTransformer<T> GetTransformer(DeviceTypeEnum device)
{
return _create(device);
}
可以改为:
// (Injected via Ctor.)
Func<IMessageTransformer<T>> _create;
public IMessageTransformer<T> GetTransformer(DeviceTypeEnum device)
{
return _create();
}
然后您可以使用 RegisterAssemblyTypes().AsClosedTypesOf(typeof(IMessageTransformer<>))
.
注册您的转换器
即,从 DeviceType
到消息类型的映射在哪里发挥作用并不是很清楚...
我想映射有一个很好的理由,但这个例子并没有真正显示它适合的位置。映射可以在 Autofac 之外,在调用 [=14= 的常规 ol' 方法中完成吗] 在容器上?
好吧,在与您交谈后,听起来您对转变的实际发生方式的一些调整持开放态度。因此,我为您构建了一个解决方案,它使用 DeviceTypeEnum
旁边的属性来解析您的转换器。工厂大致遵循抽象工厂模式。工厂负责确定需要创建的实际类型是什么;然后它将该类型的实例化委托给委托。在这种情况下,Autofac.
概述
为了让它工作,我不得不添加一个额外的界面。 IMessageTransformer
。然后我让 IMessageTransformer<T>
从它继承。基本接口只不过是一个标记接口,这样我就可以使工厂本身成为非通用的,连同 Func
委托成为非通用的。您现在可以对多个 IMessageTransformer<T>
请求使用同一个工厂实例。您也可以根据需要只实例化工厂,而不是注入它,因为它没有任何依赖关系。
为了展示工厂实际工作,我已经为它写了一个单元测试。
[TestMethod]
public void Shared_factory_instance_resolves_multiple_transformers()
{
// Arrange
var factory = new MessageTransformFactory();
IMessageTransformer<AMessage> aTransformer = factory.CreateTransformer<AMessage>(DeviceTypeEnum.Foo);
IMessageTransformer<BMessage> bTransformer = factory.CreateTransformer<BMessage>(DeviceTypeEnum.Bar);
// Act
AMessage aMessage = aTransformer.Transform(new IncomingFooMessage());
BMessage bMessage = bTransformer.Transform(new IncomingBarMessage());
// Assert
Assert.IsNotNull(aMessage, "Transformer failed to convert the IncomingMessage");
Assert.IsNotNull(bMessage, "Transformer failed to convert the IncomingMessage");
}
请注意,本可以 简化,因为引入了一个属性,将 DeviceTypeEnum
一起删除。但是我把它留在原地,因为我不确定你的总要求是什么。
实施
现在回顾一下实际的实施。我在原始问题中获取了您的示例代码,并用它构建了一个示例项目。包含实现的示例项目是 available on GitHub 供您下拉。所有源代码如下所示; GitHub 存储库只是为了让您可以下载存储库并查看运行中的代码,确保它执行您想要的操作,然后再将其实现到您的项目中。
设备类型枚举
我做的第一件事是创建 enum
,它将用于 linking 变压器及其 IIncomingMessage
和 Message
类。
public enum DeviceTypeEnum
{
Foo,
Bar,
}
留言类
接下来,我创建了两条消息 类,您要将其 转换为 。
public class AMessage
{
}
public class BMessage
{
}
IIncomingMessage
接下来是IIncomingMessage
界面。该接口只包含属性、DeviceType
。然后我创建了这个接口的两个实现,以便我可以测试实际的转换。
public interface IIncomingMessage
{
DeviceTypeEnum DeviceType { get; }
}
public class IncomingFooMessage : IIncomingMessage
{
public DeviceTypeEnum DeviceType { get { return DeviceTypeEnum.Foo; } }
}
public class IncomingBarMessage : IIncomingMessage
{
public DeviceTypeEnum DeviceType { get { return DeviceTypeEnum.Bar; } }
}
IMessageTransformer
您在示例中提供的 IMessageTransformer<T>
已被修改为继承自接口的非通用变体。
public interface IMessageTransformer
{
}
public interface IMessageTransformer<T> : IMessageTransformer where T : class
{
T Transform(IIncomingMessage message);
}
这允许您指定 Autofac 在解析期间使用的委托方法,而不是通用委托。意思是您现在使用 Func<IMessageTransformer>
而不是 Func<IMessageTransformer<T>>
。这使您可以重复使用同一个工厂实例,因为该实例未绑定到特定的通用类型。
TransformableAttribute
现在我们需要创建一个属性。此属性将用于告诉每个转换器,他们需要支持什么DeviceTypeEnum
。
[AttributeUsage(AttributeTargets.Class)]
public class TransformableAttribute : Attribute
{
public TransformableAttribute(DeviceTypeEnum deviceType)
{
this.DeviceType = deviceType;
}
public DeviceTypeEnum DeviceType { get; private set; }
}
如果DeviceTypeEnum
只是为了方便这个转换映射,那么你真的可以把枚举一起删除。您可以将 DeviceType
属性 更改为 public Type TargetType {get; private set;}
。然后工厂(如下所示)将使用 TargetType 解析(简化我创建的工厂)。由于您的示例显示正在使用此枚举,因此我将其保留为要求。
请注意,删除枚举并使用 TargetType
属性 还可以让您构建更复杂的关系,并且不必在每次必须创建新映射时都更新枚举。而不是做:
var factory = new MessageTransformFactory();
IMessageTransformer<AMessage> aTransformer = factory.CreateTransformer<AMessage>(DeviceTypeEnum.Foo);
它会让你把参数全部丢掉。不需要枚举,因为属性会让工厂知道每个转换器的目标转换结果需要是什么。
var factory = new MessageTransformFactory();
IMessageTransformer<AMessage> aTransformer = factory.CreateTransformer<AMessage>();
消息转换器
我们有属性和接口,所以我们可以继续创建几个转换器,它们将从 IIncomingMessage
转换为 AMessage
或 BMessage
。 TransformableAttribute
将告诉转换器支持哪种设备类型。转换器本身不依赖于属性;我们将用于解析的工厂,将需要 link 设备类型到变压器的属性。
[Transformable(DeviceTypeEnum.Foo)] // or [Transformable(typeof(AMessage)] if you replace the enum with a Target Type
public class AMessageTransformer : IMessageTransformer<AMessage>
{
public AMessage Transform(IIncomingMessage message)
{
if (!(message is IncomingFooMessage))
{
throw new InvalidCastException("Message was not an IncomingFooMessage");
}
return new AMessage();
}
}
[Transformable(DeviceTypeEnum.Bar)]
public class BMessageTransformer : IMessageTransformer<BMessage>
{
public BMessage Transform(IIncomingMessage message)
{
if (!(message is IncomingBarMessage))
{
throw new InvalidCastException("Message was not an IncomingBarMessage");
}
return new BMessage();
}
}
MessageTransformFactory
现在是肉和土豆。工厂负责一些事情。它必须首先扫描一组程序集,以便找到它可以创建的所有转换器 Type
。它还需要接受一个委托工厂,它将用于实例化它在需要时缓存的转换器Type
。
MessageTransformFactory
松散地遵循抽象工厂模式。它将转换对象的实际创建委托给其他东西,在本例中是 Autofac。
public class MessageTransformFactory
{
/// <summary>
/// The assemblies to cache. Defaults to including the assembly this factory exists in.
/// if there are additional assemblies that hold transformers, they can be added via the
/// MessageTransformFactory.ScanAssembly(Assembly) method.
/// </summary>
private static List<Assembly> assembliesToCache
= new List<Assembly> { typeof(MessageTransformFactory).GetTypeInfo().Assembly };
/// <summary>
/// The factory method used to instance a transformer
/// </summary>
private static Func<Type, IMessageTransformer> factoryMethod;
/// <summary>
/// The DeviceType to Transformer mapping cache
/// </summary>
private static Dictionary<DeviceTypeEnum, Type> deviceTransformerMapCache
= new Dictionary<DeviceTypeEnum, Type>();
/// <summary>
/// Initializes the <see cref="CommandFormatterFactory"/> class.
/// This will build the initial device to transformer mapping when the
/// Factory is first used.
/// </summary>
static MessageTransformFactory()
{
BuildCache();
}
/// <summary>
/// Sets the transformer factory used to instance transformers.
/// </summary>
/// <param name="factory">The factory delegate used to instance new IMessageTransformer objects.</param>
public static void SetTransformerFactory(Func<Type, IMessageTransformer> factory)
{
if (factory == null)
{
throw new ArgumentNullException("factory", "Factory delegate can not be null.");
}
MessageTransformFactory.factoryMethod = factory;
}
/// <summary>
/// Scans a given assembly for IMessageTransformer implementations.
/// </summary>
/// <param name="assemblyName">Name of the assembly to scan.</param>
public static void ScanAssembly(AssemblyName assemblyName)
{
if (assemblyName == null)
{
throw new ArgumentNullException("assemblyName", "A valid assembly name must be provided.");
}
Assembly assembly = Assembly.Load(assemblyName);
if (assembliesToCache.Any(a => a.FullName == assemblyName.FullName))
{
return;
}
assembliesToCache.Add(assembly);
MapDeviceTypesFromAssembly(assembly);
}
/// <summary>
/// Gets the available transformer types that have been registered to this factory.
/// </summary>
public static Type[] GetAvailableTransformerTypes()
{
return deviceTransformerMapCache.Values.ToArray();
}
/// <summary>
/// Gets an IMessageTransformer implementation for the Device Type given.
/// </summary>
/// <param name="deviceType">The DeviceType that the factory must create an IMessageTransformer for.</param>
public IMessageTransformer<T> CreateTransformer<T>(DeviceTypeEnum deviceType) where T : class
{
// If we have a factory method, then we use it.
if (factoryMethod == null)
{
throw new NullReferenceException("The MessageTransformerFactory did not have its factory method set.");
}
// Cast the non-generic return value to the generic version for the caller.
Type transformerType = MessageTransformFactory.deviceTransformerMapCache[deviceType];
return factoryMethod(transformerType) as IMessageTransformer<T>;
}
/// <summary>
/// Builds the cache of IMessageTransformer Types that can be used by this factory.
/// </summary>
private static void BuildCache()
{
foreach (var assembly in assembliesToCache)
{
MapDeviceTypesFromAssembly(assembly);
}
}
/// <summary>
/// Creates a DeviceType to IMessageTransformer Type mapping.
/// </summary>
/// <param name="assembly"></param>
private static void MapDeviceTypesFromAssembly(Assembly assembly)
{
var transformableTypes = assembly.DefinedTypes
.Where(type => type
.ImplementedInterfaces
.Any(inter => inter == typeof(IMessageTransformer)) && !type.IsAbstract);
foreach (TypeInfo transformer in transformableTypes)
{
var commandCode = transformer.GetCustomAttribute<TransformableAttribute>();
deviceTransformerMapCache.Add(commandCode.DeviceType, transformer.AsType());
}
}
}
Autofac 设置
有了这个实现,映射的责任现在就落在了工厂身上。这样一来,我们的Autofac注册就变得非常简单了。
var builder = new ContainerBuilder();
// Register all of the available transformers.
builder
.RegisterTypes(MessageTransformFactory.GetAvailableTransformerTypes())
.AsImplementedInterfaces()
.AsSelf();
// Build the IoC container
this.container = builder.Build();
// Define our factory method for resolving the transformer based on device type.
MessageTransformFactory.SetTransformerFactory((type) =>
{
if (!type.IsAssignableTo<IMessageTransformer>())
{
throw new InvalidOperationException("The type provided to the message transform factory resolver can not be cast to IMessageTransformer");
}
return container.Resolve(type) as IMessageTransformer;
});
完成 Autofac 注册后,我们将设置 MessageTransformFactory
的委托工厂方法来解析工厂使用的类型。
这也适用于测试,因为您现在可以自定义工厂在单元测试期间使用的实例化过程。您可以通过让测试调用 MessageTransformFactory.SetTransformFactory()
创建模拟转换器并构建一个 returns 模拟的简单委托
最后,您提到您更喜欢注入工厂以遵循您已经使用的约定。虽然这个工厂没有任何依赖关系(因此可以在不需要任何 IoC 的情况下进行实例化),但您可以抽象它具有的第一个实例方法,即 CreateTransformer<T>
方法,位于接口后面。然后,您使用该接口向 Autofac 注册工厂并注入它。这样,没有什么是工厂的具体实现。这也阻止了其他人访问工厂必须具有的静态方法,以便于与 Autofac 进行映射和耦合。
我正在尝试创建一个工厂来帮助将基于接口 (IIncomingMessage) 的 class 转换为其他基于 classes (AMessage, BMessage) 的新实例单曲class的18=],喜欢:
public interface IIncomingMessage
{
public DeviceTypeEnum DeviceType {get;}
}
public class IncomingMessage : IIncomingMessage
{
public DeviceTypeEnum DeviceType {get {return DeviceTypeEnum.TypeA;}}
Public Byte[] RawData {get; set;}
}
public interface IMessageTransformer<out T> where T:class
{
T Transform(IIncomingMessage message);
}
public class AMessage
{
public int ChoppedUp1 {get; set;}
public int ChoppedUp2 {get; set;}
public int ChoppedUp3 {get; set;}
}
public class BMessage
{
public string SomeData {get; set;}
public string SomeMoreData {get; set;}
}
public class AMessageTransformer : IMessageTransformer<AMessage>
{
public AMessage Transform(IIncomingMessage message)
{
var result = new AMessage();
result.ChoppedUp1 = message.RawData[1];
result.ChoppedUp2 = message.RawData[2];
....
return result;
}
}
public class BMessageTransformation : IMessageTransformer<BMessage>
{
public BMessage Transform(IIncomingMessage message)
{
throw new NotImplementedException();
}
}
public class MessageTransformFactory<T> where T : class
{
private readonly Func<DeviceTypeEnum, IMessageTransformer<T>> _create;
public MessageTransformFactory(Func<DeviceTypeEnum, IMessageTransformer<T>> create)
{
_create = create;
}
public IMessageTransformer<T> GetTransformer(DeviceTypeEnum device)
{
return _create(device);
}
}
我正在使用 Autofac,我很确定应该有一种方法可以创建一个工厂,它可以根据 DeviceType 的 属性 值返回正确的变压器。使用并看到了大量使用对象类型而不是值来做出决定的示例。 Named and Keyed Services 看起来很有前途,但看起来我仍然在某处得到一个静态列表。
根据您提供的示例,它看起来像:
public IMessageTransformer<T> GetTransformer(DeviceTypeEnum device)
{
return _create(device);
}
可以改为:
// (Injected via Ctor.)
Func<IMessageTransformer<T>> _create;
public IMessageTransformer<T> GetTransformer(DeviceTypeEnum device)
{
return _create();
}
然后您可以使用 RegisterAssemblyTypes().AsClosedTypesOf(typeof(IMessageTransformer<>))
.
即,从 DeviceType
到消息类型的映射在哪里发挥作用并不是很清楚...
我想映射有一个很好的理由,但这个例子并没有真正显示它适合的位置。映射可以在 Autofac 之外,在调用 [=14= 的常规 ol' 方法中完成吗] 在容器上?
好吧,在与您交谈后,听起来您对转变的实际发生方式的一些调整持开放态度。因此,我为您构建了一个解决方案,它使用 DeviceTypeEnum
旁边的属性来解析您的转换器。工厂大致遵循抽象工厂模式。工厂负责确定需要创建的实际类型是什么;然后它将该类型的实例化委托给委托。在这种情况下,Autofac.
概述
为了让它工作,我不得不添加一个额外的界面。 IMessageTransformer
。然后我让 IMessageTransformer<T>
从它继承。基本接口只不过是一个标记接口,这样我就可以使工厂本身成为非通用的,连同 Func
委托成为非通用的。您现在可以对多个 IMessageTransformer<T>
请求使用同一个工厂实例。您也可以根据需要只实例化工厂,而不是注入它,因为它没有任何依赖关系。
为了展示工厂实际工作,我已经为它写了一个单元测试。
[TestMethod]
public void Shared_factory_instance_resolves_multiple_transformers()
{
// Arrange
var factory = new MessageTransformFactory();
IMessageTransformer<AMessage> aTransformer = factory.CreateTransformer<AMessage>(DeviceTypeEnum.Foo);
IMessageTransformer<BMessage> bTransformer = factory.CreateTransformer<BMessage>(DeviceTypeEnum.Bar);
// Act
AMessage aMessage = aTransformer.Transform(new IncomingFooMessage());
BMessage bMessage = bTransformer.Transform(new IncomingBarMessage());
// Assert
Assert.IsNotNull(aMessage, "Transformer failed to convert the IncomingMessage");
Assert.IsNotNull(bMessage, "Transformer failed to convert the IncomingMessage");
}
请注意,本可以 简化,因为引入了一个属性,将 DeviceTypeEnum
一起删除。但是我把它留在原地,因为我不确定你的总要求是什么。
实施
现在回顾一下实际的实施。我在原始问题中获取了您的示例代码,并用它构建了一个示例项目。包含实现的示例项目是 available on GitHub 供您下拉。所有源代码如下所示; GitHub 存储库只是为了让您可以下载存储库并查看运行中的代码,确保它执行您想要的操作,然后再将其实现到您的项目中。
设备类型枚举
我做的第一件事是创建 enum
,它将用于 linking 变压器及其 IIncomingMessage
和 Message
类。
public enum DeviceTypeEnum
{
Foo,
Bar,
}
留言类
接下来,我创建了两条消息 类,您要将其 转换为 。
public class AMessage
{
}
public class BMessage
{
}
IIncomingMessage
接下来是IIncomingMessage
界面。该接口只包含属性、DeviceType
。然后我创建了这个接口的两个实现,以便我可以测试实际的转换。
public interface IIncomingMessage
{
DeviceTypeEnum DeviceType { get; }
}
public class IncomingFooMessage : IIncomingMessage
{
public DeviceTypeEnum DeviceType { get { return DeviceTypeEnum.Foo; } }
}
public class IncomingBarMessage : IIncomingMessage
{
public DeviceTypeEnum DeviceType { get { return DeviceTypeEnum.Bar; } }
}
IMessageTransformer
您在示例中提供的 IMessageTransformer<T>
已被修改为继承自接口的非通用变体。
public interface IMessageTransformer
{
}
public interface IMessageTransformer<T> : IMessageTransformer where T : class
{
T Transform(IIncomingMessage message);
}
这允许您指定 Autofac 在解析期间使用的委托方法,而不是通用委托。意思是您现在使用 Func<IMessageTransformer>
而不是 Func<IMessageTransformer<T>>
。这使您可以重复使用同一个工厂实例,因为该实例未绑定到特定的通用类型。
TransformableAttribute
现在我们需要创建一个属性。此属性将用于告诉每个转换器,他们需要支持什么DeviceTypeEnum
。
[AttributeUsage(AttributeTargets.Class)]
public class TransformableAttribute : Attribute
{
public TransformableAttribute(DeviceTypeEnum deviceType)
{
this.DeviceType = deviceType;
}
public DeviceTypeEnum DeviceType { get; private set; }
}
如果DeviceTypeEnum
只是为了方便这个转换映射,那么你真的可以把枚举一起删除。您可以将 DeviceType
属性 更改为 public Type TargetType {get; private set;}
。然后工厂(如下所示)将使用 TargetType 解析(简化我创建的工厂)。由于您的示例显示正在使用此枚举,因此我将其保留为要求。
请注意,删除枚举并使用 TargetType
属性 还可以让您构建更复杂的关系,并且不必在每次必须创建新映射时都更新枚举。而不是做:
var factory = new MessageTransformFactory();
IMessageTransformer<AMessage> aTransformer = factory.CreateTransformer<AMessage>(DeviceTypeEnum.Foo);
它会让你把参数全部丢掉。不需要枚举,因为属性会让工厂知道每个转换器的目标转换结果需要是什么。
var factory = new MessageTransformFactory();
IMessageTransformer<AMessage> aTransformer = factory.CreateTransformer<AMessage>();
消息转换器
我们有属性和接口,所以我们可以继续创建几个转换器,它们将从 IIncomingMessage
转换为 AMessage
或 BMessage
。 TransformableAttribute
将告诉转换器支持哪种设备类型。转换器本身不依赖于属性;我们将用于解析的工厂,将需要 link 设备类型到变压器的属性。
[Transformable(DeviceTypeEnum.Foo)] // or [Transformable(typeof(AMessage)] if you replace the enum with a Target Type
public class AMessageTransformer : IMessageTransformer<AMessage>
{
public AMessage Transform(IIncomingMessage message)
{
if (!(message is IncomingFooMessage))
{
throw new InvalidCastException("Message was not an IncomingFooMessage");
}
return new AMessage();
}
}
[Transformable(DeviceTypeEnum.Bar)]
public class BMessageTransformer : IMessageTransformer<BMessage>
{
public BMessage Transform(IIncomingMessage message)
{
if (!(message is IncomingBarMessage))
{
throw new InvalidCastException("Message was not an IncomingBarMessage");
}
return new BMessage();
}
}
MessageTransformFactory
现在是肉和土豆。工厂负责一些事情。它必须首先扫描一组程序集,以便找到它可以创建的所有转换器 Type
。它还需要接受一个委托工厂,它将用于实例化它在需要时缓存的转换器Type
。
MessageTransformFactory
松散地遵循抽象工厂模式。它将转换对象的实际创建委托给其他东西,在本例中是 Autofac。
public class MessageTransformFactory
{
/// <summary>
/// The assemblies to cache. Defaults to including the assembly this factory exists in.
/// if there are additional assemblies that hold transformers, they can be added via the
/// MessageTransformFactory.ScanAssembly(Assembly) method.
/// </summary>
private static List<Assembly> assembliesToCache
= new List<Assembly> { typeof(MessageTransformFactory).GetTypeInfo().Assembly };
/// <summary>
/// The factory method used to instance a transformer
/// </summary>
private static Func<Type, IMessageTransformer> factoryMethod;
/// <summary>
/// The DeviceType to Transformer mapping cache
/// </summary>
private static Dictionary<DeviceTypeEnum, Type> deviceTransformerMapCache
= new Dictionary<DeviceTypeEnum, Type>();
/// <summary>
/// Initializes the <see cref="CommandFormatterFactory"/> class.
/// This will build the initial device to transformer mapping when the
/// Factory is first used.
/// </summary>
static MessageTransformFactory()
{
BuildCache();
}
/// <summary>
/// Sets the transformer factory used to instance transformers.
/// </summary>
/// <param name="factory">The factory delegate used to instance new IMessageTransformer objects.</param>
public static void SetTransformerFactory(Func<Type, IMessageTransformer> factory)
{
if (factory == null)
{
throw new ArgumentNullException("factory", "Factory delegate can not be null.");
}
MessageTransformFactory.factoryMethod = factory;
}
/// <summary>
/// Scans a given assembly for IMessageTransformer implementations.
/// </summary>
/// <param name="assemblyName">Name of the assembly to scan.</param>
public static void ScanAssembly(AssemblyName assemblyName)
{
if (assemblyName == null)
{
throw new ArgumentNullException("assemblyName", "A valid assembly name must be provided.");
}
Assembly assembly = Assembly.Load(assemblyName);
if (assembliesToCache.Any(a => a.FullName == assemblyName.FullName))
{
return;
}
assembliesToCache.Add(assembly);
MapDeviceTypesFromAssembly(assembly);
}
/// <summary>
/// Gets the available transformer types that have been registered to this factory.
/// </summary>
public static Type[] GetAvailableTransformerTypes()
{
return deviceTransformerMapCache.Values.ToArray();
}
/// <summary>
/// Gets an IMessageTransformer implementation for the Device Type given.
/// </summary>
/// <param name="deviceType">The DeviceType that the factory must create an IMessageTransformer for.</param>
public IMessageTransformer<T> CreateTransformer<T>(DeviceTypeEnum deviceType) where T : class
{
// If we have a factory method, then we use it.
if (factoryMethod == null)
{
throw new NullReferenceException("The MessageTransformerFactory did not have its factory method set.");
}
// Cast the non-generic return value to the generic version for the caller.
Type transformerType = MessageTransformFactory.deviceTransformerMapCache[deviceType];
return factoryMethod(transformerType) as IMessageTransformer<T>;
}
/// <summary>
/// Builds the cache of IMessageTransformer Types that can be used by this factory.
/// </summary>
private static void BuildCache()
{
foreach (var assembly in assembliesToCache)
{
MapDeviceTypesFromAssembly(assembly);
}
}
/// <summary>
/// Creates a DeviceType to IMessageTransformer Type mapping.
/// </summary>
/// <param name="assembly"></param>
private static void MapDeviceTypesFromAssembly(Assembly assembly)
{
var transformableTypes = assembly.DefinedTypes
.Where(type => type
.ImplementedInterfaces
.Any(inter => inter == typeof(IMessageTransformer)) && !type.IsAbstract);
foreach (TypeInfo transformer in transformableTypes)
{
var commandCode = transformer.GetCustomAttribute<TransformableAttribute>();
deviceTransformerMapCache.Add(commandCode.DeviceType, transformer.AsType());
}
}
}
Autofac 设置
有了这个实现,映射的责任现在就落在了工厂身上。这样一来,我们的Autofac注册就变得非常简单了。
var builder = new ContainerBuilder();
// Register all of the available transformers.
builder
.RegisterTypes(MessageTransformFactory.GetAvailableTransformerTypes())
.AsImplementedInterfaces()
.AsSelf();
// Build the IoC container
this.container = builder.Build();
// Define our factory method for resolving the transformer based on device type.
MessageTransformFactory.SetTransformerFactory((type) =>
{
if (!type.IsAssignableTo<IMessageTransformer>())
{
throw new InvalidOperationException("The type provided to the message transform factory resolver can not be cast to IMessageTransformer");
}
return container.Resolve(type) as IMessageTransformer;
});
完成 Autofac 注册后,我们将设置 MessageTransformFactory
的委托工厂方法来解析工厂使用的类型。
这也适用于测试,因为您现在可以自定义工厂在单元测试期间使用的实例化过程。您可以通过让测试调用 MessageTransformFactory.SetTransformFactory()
最后,您提到您更喜欢注入工厂以遵循您已经使用的约定。虽然这个工厂没有任何依赖关系(因此可以在不需要任何 IoC 的情况下进行实例化),但您可以抽象它具有的第一个实例方法,即 CreateTransformer<T>
方法,位于接口后面。然后,您使用该接口向 Autofac 注册工厂并注入它。这样,没有什么是工厂的具体实现。这也阻止了其他人访问工厂必须具有的静态方法,以便于与 Autofac 进行映射和耦合。