我有以下代码:
public class Temp<T, TMetadata>
{
[ImportMany]
private IEnumerable<Lazy<T, TMetadata>> plugins;
public Temp(string path)
{
AggregateCatalog aggregateCatalog = new AggregateCatalog();
aggregateCatalog.Catalogs.Add(new DirectoryCatalog(path));
CompositionContainer container = new CompositionContainer(aggregateCatalog);
container.ComposeParts(this);
}
public T GetPlugin(Predicate<TMetadata> predicate)
{
Lazy<T, TMetadata> pluginInfo;
try
{
pluginInfo = plugins.SingleOrDefault(p => predicate(p.Metadata));
}
catch
{
// throw some exception
}
if (pluginInfo == null)
{
// throw some exception
}
return Clone(pluginInfo.Value); // -> this produces errors
}
}
我有一个Temp
的对象,我从多个线程调用GetPlugin()
。有时我会遇到奇怪的构图错误,我没有找到重现的方法。例如:
"System.InvalidOperationException: Stack empty.
at System.Collections.Generic.Stack`1.Pop()
at System.ComponentModel.Composition.Hosting.ImportEngine.TrySatisfyImports(PartManager partManager, ComposablePart part, Boolean shouldTrackImports)
at System.ComponentModel.Composition.Hosting.ImportEngine.SatisfyImports(ComposablePart part)
at System.ComponentModel.Composition.Hosting.CompositionServices.GetExportedValueFromComposedPart(ImportEngine engine, ComposablePart part, ExportDefinition definition)
at System.ComponentModel.Composition.Hosting.CatalogExportProvider.GetExportedValue(CatalogPart part, ExportDefinition export, Boolean isSharedPart)
at System.ComponentModel.Composition.ExportServices.GetCastedExportedValue[T](Export export)
at System.Lazy`1.CreateValue()
at System.Lazy`1.LazyInitValue()
at Temp`2.GetPlugin(Predicate`1 predicate)..."
可能是什么原因以及如何解决此代码?
答案 0 :(得分:19)
CompositionContainer
类的little-known constructor接受isThreadSafe
参数(出于性能原因,默认为false)。如果您将此值设置为true来创建容器,我相信您的问题将得到解决:
CompositionContainer container = new CompositionContainer(aggregateCatalog, true);
在旁注上,与原始问题无关,而不是在插件上调用Clone()
,而是使用an export factory代替 - 这样您就不必实现自己的克隆方法了,因为MEF会为你创建一个新实例。
答案 1 :(得分:0)
如果您想获得匹配导入类型的可用导出列表,则无需使用(有问题的)container.ComposeParts(this);
你可以做更多的事情:
var pluginsAvailable = container.GetExports<T>().Select(y => y.Value).ToArray();
这将为您提供一系列可用实例,而不会出现困扰MEF的所有线程问题。
我今天一直在做这样的事情......请原谅代码转储:
using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Threading;
namespace PluginWatcher
{
/// <summary>
/// Watch for changes to a plugin directory for a specific MEF Import type.
/// <para>Keeps a list of last seen exports and exposes a change event</para>
/// </summary>
/// <typeparam name="T">Plugin type. Plugins should contain classes implementing this type and decorated with [Export(typeof(...))]</typeparam>
public interface IPluginWatcher<T> : IDisposable
{
/// <summary>
/// Available Exports matching type <typeparamref name="T"/> have changed
/// </summary>
event EventHandler<PluginsChangedEventArgs<T>> PluginsChanged;
/// <summary>
/// Last known Exports matching type <typeparamref name="T"/>.
/// </summary>
IEnumerable<T> CurrentlyAvailable { get; }
}
/// <summary>
/// Event arguments relating to a change in available MEF Export types.
/// </summary>
public class PluginsChangedEventArgs<T>: EventArgs
{
/// <summary>
/// Last known Exports matching type <typeparamref name="T"/>.
/// </summary>
public IEnumerable<T> AvailablePlugins { get; set; }
}
/// <summary>
/// Watch for changes to a plugin directory for a specific MEF Import type.
/// <para>Keeps a list of last seen exports and exposes a change event</para>
/// </summary>
/// <typeparam name="T">Plugin type. Plugins should contain classes implementing this type and decorated with [Export(typeof(...))]</typeparam>
public class PluginWatcher<T> : IPluginWatcher<T>
{
private readonly object _compositionLock = new object();
private FileSystemWatcher _fsw;
private DirectoryCatalog _pluginCatalog;
private CompositionContainer _container;
private AssemblyCatalog _localCatalog;
private AggregateCatalog _catalog;
public event EventHandler<PluginsChangedEventArgs<T>> PluginsChanged;
protected virtual void OnPluginsChanged()
{
var handler = PluginsChanged;
if (handler != null) handler(this, new PluginsChangedEventArgs<T> { AvailablePlugins = CurrentlyAvailable });
}
public PluginWatcher(string pluginDirectory)
{
if (!Directory.Exists(pluginDirectory)) throw new Exception("Can't watch \"" + pluginDirectory + "\", might not exist or not enough permissions");
CurrentlyAvailable = new T[0];
_fsw = new FileSystemWatcher(pluginDirectory, "*.dll");
SetupFileWatcher();
try
{
_pluginCatalog = new DirectoryCatalog(pluginDirectory);
_localCatalog = new AssemblyCatalog(Assembly.GetExecutingAssembly());
_catalog = new AggregateCatalog();
_catalog.Catalogs.Add(_localCatalog);
_catalog.Catalogs.Add(_pluginCatalog);
_container = new CompositionContainer(_catalog, false);
_container.ExportsChanged += ExportsChanged;
}
catch
{
Dispose(true);
throw;
}
ReadLoadedPlugins();
}
private void SetupFileWatcher()
{
_fsw.NotifyFilter = NotifyFilters.Attributes | NotifyFilters.CreationTime | NotifyFilters.FileName |
NotifyFilters.LastAccess | NotifyFilters.LastWrite | NotifyFilters.Size | NotifyFilters.Security;
_fsw.Changed += FileAddedOrRemoved;
_fsw.Created += FileAddedOrRemoved;
_fsw.Deleted += FileAddedOrRemoved;
_fsw.Renamed += FileRenamed;
_fsw.EnableRaisingEvents = true;
}
private void ExportsChanged(object sender, ExportsChangeEventArgs e)
{
lock (_compositionLock)
{
if (e.AddedExports.Any() || e.RemovedExports.Any()) ReadLoadedPlugins();
}
}
private void ReadLoadedPlugins()
{
CurrentlyAvailable = _container.GetExports<T>().Select(y => y.Value).ToArray();
OnPluginsChanged();
}
private void FileRenamed(object sender, RenamedEventArgs e)
{
RefreshPlugins();
}
void FileAddedOrRemoved(object sender, FileSystemEventArgs e)
{
RefreshPlugins();
}
private void RefreshPlugins()
{
try
{
var cat = _pluginCatalog;
if (cat == null) { return; }
lock (_compositionLock)
{
cat.Refresh();
}
}
catch (ChangeRejectedException rejex)
{
Console.WriteLine("Could not update plugins: " + rejex.Message);
}
}
public IEnumerable<T> CurrentlyAvailable { get; protected set; }
~PluginWatcher()
{
Dispose(true);
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected void Dispose(bool disposing)
{
if (!disposing) return;
var fsw = Interlocked.Exchange(ref _fsw, null);
if (fsw != null) fsw.Dispose();
var plg = Interlocked.Exchange(ref _pluginCatalog, null);
if (plg != null) plg.Dispose();
var con = Interlocked.Exchange(ref _container, null);
if (con != null) con.Dispose();
var loc = Interlocked.Exchange(ref _localCatalog, null);
if (loc != null) loc.Dispose();
var cat = Interlocked.Exchange(ref _catalog, null);
if (cat != null) cat.Dispose();
}
}
}