在.NET 4.0中使用MEF來節省我大量的抽象工廠代碼和配置gubbins。由於未部署,因此無法移至.net 4.5。MEF實例和多線程
類
/// <summary>
/// Factory relies upon the use of the .net 4.0 MEF framework
/// All processors need to export themselves to make themselves visible to the 'Processors' import property auto MEF population
/// This class is implemented as a singleton
/// </summary>
public class MessageProsessorFactory
{
private static readonly string pluginFilenameFilter = "Connectors.*.dll";
private static CompositionContainer _container;
private static MessageProsessorFactory _instance;
private static object MessageProsessorFactoryLock = new object();
/// <summary>
/// Initializes the <see cref="MessageProsessorFactory" /> class.
/// Loads all MEF imports
/// </summary>
/// <exception cref="System.NotSupportedException"></exception>
private MessageProsessorFactory()
{
lock (MessageProsessorFactoryLock)
{
if (_container == null)
{
RemoveDllSecurityZoneRestrictions();
//Create a thread safe composition container
_container = new CompositionContainer(new DirectoryCatalog(".", pluginFilenameFilter), true, null);
_container.ComposeParts(this);
}
}
}
/// <summary>
/// A list of detected class instances that support IMessageProcessor
/// </summary>
[ImportMany(typeof(IMessageProcessor), RequiredCreationPolicy = CreationPolicy.NonShared)]
private List<Lazy<IMessageProcessor, IMessageProccessorExportMetadata>> Processors { get; set; }
/// <summary>
/// Gets the message factory.
/// </summary>
/// <param name="messageEnvelope">The message envelope.</param>
/// <returns><see cref="IMessageProcessor"/></returns>
/// <exception cref="System.NotSupportedException">The supplied target is not supported: + target</exception>
public static IMessageProcessor GetMessageProcessor(MessageEnvelope messageEnvelope)
{
if (_instance == null)
_instance = new MessageProsessorFactory();
var p = _instance.Processors.FirstOrDefault(
s => s.Metadata.ExpectedType.AssemblyQualifiedName == messageEnvelope.AssemblyQualifiedName);
if (p == null)
throw new NotSupportedException(
"The supplied type is not supported: " + messageEnvelope.AssemblyQualifiedName);
return p.Value;
}
/// <summary>
/// Removes any zone flags otherwise MEF wont load files with
/// a URL zone flag set to anything other than 'MyComputer', we are trusting all pluggins here.
/// http://msdn.microsoft.com/en-us/library/ms537183(v=vs.85).aspx
/// </summary>
private static void RemoveDllSecurityZoneRestrictions()
{
string path = System.IO.Path.GetDirectoryName(
System.Reflection.Assembly.GetExecutingAssembly().Location);
foreach (var filePath in Directory.EnumerateFiles(path, pluginFilenameFilter))
{
var zone = Zone.CreateFromUrl(filePath);
if (zone.SecurityZone != SecurityZone.MyComputer)
{
var fileInfo = new FileInfo(filePath);
fileInfo.DeleteAlternateDataStream("Zone.Identifier");
}
}
}
}
_container.ComposeParts(this);
之後被調用時,處理器會填充找到的所有IMessageProcessor實現。大。
注意
- GetMessageProcessor是由許多線程調用。
- 我們無法控制開發人員如何構造IMessageProcessor的實現類 ,因此我們無法保證 是線程安全的 - 可重入。但是,該類必須使用「導出」屬性進行檢測。
出口屬性
[MetadataAttribute]
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
public class MessageProccessorExportAttribute : ExportAttribute
{
public MessageProccessorExportAttribute()
: base(typeof(IMessageProcessor))
{
}
public Type ExpectedType { get; set; }
}
- ExpectedType是什麼筆記 IMessageProcessor.ProcessMessage()預計將處理,純粹 實現細節只是元數據。
我的問題是,我到處讀到每個導入的實例都是Singleton,而不管它的激活策略是通過Lazy <>引用構造的。
因此,我們不能允許從GetMessageProcessor返回MEF中的實例,因爲多個線程將獲得相同的實例,這是不可取的。唉唉! 我想知道下面的「解決方法」是否是最好的方法,或者我有MEF堅持概念錯誤。
我的解決方法是將看似毫無意義的RequiredCreationPolicy = CreationPolicy.NonShared
屬性設置更改爲CreationPolicy.Shared
。
然後改變函數GetMessageProcessor手動創建一個新的實例,真正從MEF中分離出來。使用MEF共享實例prulry作爲類型列表。
IMessageProcessor newInstance = (IMessageProcessor)Activator.CreateInstance(p.Value.GetType());
完整的方法;
public static IMessageProcessor GetMessageProcessor(MessageEnvelope messageEnvelope)
{
if (_instance == null)
_instance = new MessageProsessorFactory();
var p = _instance.Processors.FirstOrDefault(
s => s.Metadata.ExpectedType.AssemblyQualifiedName == messageEnvelope.AssemblyQualifiedName);
if (p == null)
throw new NotSupportedException(
"The supplied type is not supported: " + messageEnvelope.AssemblyQualifiedName);
// we need to create a new instance from the singleton instance provided by MEF to assure we get a instance un-associated with the MEF container for
// currently as of .net 4.0 it wants to keep references on objects which may impact memory consumption.
// As we have no control over how a developer will organise there class that exposes an Export,
// this could lead to multithreading issues as an imported lazy instance is a singleton regardless
// of the RequiredCreationPolicy.
// MEF is still invaluable in avoided a tone of abstract factory code and its dynamic detection of all supporting
// Exports conforming to IMessageProcessor means there is no factory config for us to maintain.
IMessageProcessor newInstance = (IMessageProcessor)Activator.CreateInstance(p.Value.GetType());
return newInstance;
}
不會這種方法失敗,如果一個'IMessageProcessor'實例都有一個'ImportingConstructor'而不是默認的構造函數?此外,它不會滿足新實例的任何導入。 –