MEF的多次出口确实有些令人发指的东西 - 为什么,为什么允许它?

时间:2010-04-20 19:23:18

标签: wpf inversion-of-control export mef

我有一个有趣的情况,我需要做这样的事情:

[Export[typeof(ICandy1)]
[Export[typeof(ICandy2)]
public class Candy : ICandy2 { ... }

其中

public interface ICandy1 { ... }
public interface ICandy2 : ICandy1 { ... }

我无法在任何地方找到关于使用多个[导出]属性的任何帖子,所以我想,不管怎样,不妨尝试一下。

乍一看,它实际上似乎有效。我有两个调用Candy实例的两个接口的方法,很好。

但是,当我开始测试应用程序时,我看到行为不正确,当查看“输出”窗口时,我看到我得到了的COMExceptions。我无法追踪它们的来源,但它们总是在工人线程正在睡觉时发生。我认为它必须来自主线程,然后,但根本不知道如何调试它。 GUI中没有任何内容,我禁用了我的DispatchTimers以防万一 - 同样的事情。

比COMExceptions更奇怪的是在单步执行代码时真的,非常不稳定的行为。大约30%的时间,当我单步执行时,它会弹出方法,或者它会单步执行两行代码!完全不奇怪的东西,我不习惯看到。

在工作代码和非工作代码之间唯一改变的是通过我的插件加载代码引入MEF。因此,作为测试,我将插件程序集更改为仅导出一个接口,并且我在应用程序中硬编码了依赖于另一个(现在未实现的)接口的所有内容。现在COMExceptions消失了,奇怪的调试行为也消失了。

这是人们以前见过的东西吗?如果不希望MEF允许类导出多个接口,那么在编写部件时不应该引发CompositionException吗?谁能解释为什么MEF会引起这些奇怪的问题?

这是一个关于COMException时主线程调用堆栈的示例。不确定这对任何人是否意味着什么,但如果你能提出任何调试方法,那就太好了。

> UIAutomationProvider.dll!MS.Internal.Automation.UiaCoreProviderApi.UiaHostProviderFromHwnd(System.IntPtr hwnd) + 0x38 bytes 
  UIAutomationProvider.dll!System.Windows.Automation.Provider.AutomationInteropProvider.HostProviderFromHandle(System.IntPtr hwnd) + 0x2d bytes 
  PresentationCore.dll!MS.Internal.Automation.ElementProxy.HostRawElementProvider.get() + 0x65 bytes 
  [Native to Managed Transition] 
  [Managed to Native Transition] 
  UIAutomationProvider.dll!System.Windows.Automation.Provider.AutomationInteropProvider.RaiseAutomationPropertyChangedEvent(System.Windows.Automation.Provider.IRawElementProviderSimple element, System.Windows.Automation.AutomationPropertyChangedEventArgs e) + 0x2a bytes 
  PresentationCore.dll!System.Windows.Automation.Peers.AutomationPeer.UpdateSubtree() + 0x2c9 bytes 
  PresentationCore.dll!System.Windows.Automation.Peers.AutomationPeer.UpdateSubtree() + 0x2f8 bytes 
  PresentationCore.dll!System.Windows.Automation.Peers.AutomationPeer.UpdateSubtree() + 0x2f8 bytes 
  PresentationCore.dll!System.Windows.Automation.Peers.AutomationPeer.UpdateSubtree() + 0x2f8 bytes 
  PresentationCore.dll!System.Windows.ContextLayoutManager.fireAutomationEvents() + 0x98 bytes 
  PresentationCore.dll!System.Windows.ContextLayoutManager.UpdateLayout() + 0x65b bytes 
  PresentationCore.dll!System.Windows.ContextLayoutManager.UpdateLayoutCallback(object arg) + 0x19 bytes 
  PresentationCore.dll!System.Windows.Media.MediaContext.InvokeOnRenderCallback.DoWork() + 0x10 bytes 
  PresentationCore.dll!System.Windows.Media.MediaContext.FireInvokeOnRenderCallbacks() + 0x97 bytes 
  PresentationCore.dll!System.Windows.Media.MediaContext.RenderMessageHandlerCore(object resizedCompositionTarget = null) + 0x80 bytes 
  PresentationCore.dll!System.Windows.Media.MediaContext.RenderMessageHandler(object resizedCompositionTarget) + 0x2b bytes 
  WindowsBase.dll!System.Windows.Threading.ExceptionWrapper.InternalRealCall(System.Delegate callback = {Method = Cannot evaluate expression because the code of the current method is optimized.}, object args = null, bool isSingleParameter = true) + 0x8a bytes 
  WindowsBase.dll!System.Windows.Threading.ExceptionWrapper.TryCatchWhen(object source = {System.Windows.Threading.Dispatcher}, System.Delegate callback, object args, bool isSingleParameter, System.Delegate catchHandler = null) + 0x4a bytes 
  WindowsBase.dll!System.Windows.Threading.Dispatcher.WrappedInvoke(System.Delegate callback, object args, bool isSingleParameter, System.Delegate catchHandler) + 0x44 bytes 
  WindowsBase.dll!System.Windows.Threading.DispatcherOperation.InvokeImpl() + 0x5d bytes 
  WindowsBase.dll!System.Windows.Threading.DispatcherOperation.InvokeInSecurityContext(object state) + 0x38 bytes 
  mscorlib.dll!System.Threading.ExecutionContext.runTryCode(object userData) + 0x51 bytes 
  [Native to Managed Transition] 
  [Managed to Native Transition] 
  mscorlib.dll!System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, object state) + 0x67 bytes 
  mscorlib.dll!System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, object state) + 0x45 bytes 
  WindowsBase.dll!System.Windows.Threading.DispatcherOperation.Invoke() + 0x63 bytes 
  WindowsBase.dll!System.Windows.Threading.Dispatcher.ProcessQueue() + 0x127 bytes 
  WindowsBase.dll!System.Windows.Threading.Dispatcher.WndProcHook(System.IntPtr hwnd, int msg, System.IntPtr wParam, System.IntPtr lParam, ref bool handled) + 0x63 bytes 
  WindowsBase.dll!MS.Win32.HwndWrapper.WndProc(System.IntPtr hwnd = 464158, int msg = 49869, System.IntPtr wParam = 0, System.IntPtr lParam = 0, ref bool handled = false) + 0xbe bytes 
  WindowsBase.dll!MS.Win32.HwndSubclass.DispatcherCallbackOperation(object o) + 0x7a bytes 
  WindowsBase.dll!System.Windows.Threading.ExceptionWrapper.InternalRealCall(System.Delegate callback = {Method = Cannot evaluate expression because the code of the current method is optimized.}, object args = {MS.Win32.HwndSubclass.DispatcherOperationCallbackParameter}, bool isSingleParameter = true) + 0x8a bytes 
  WindowsBase.dll!System.Windows.Threading.ExceptionWrapper.TryCatchWhen(object source = {System.Windows.Threading.Dispatcher}, System.Delegate callback, object args, bool isSingleParameter, System.Delegate catchHandler = null) + 0x4a bytes 
  WindowsBase.dll!System.Windows.Threading.Dispatcher.WrappedInvoke(System.Delegate callback, object args, bool isSingleParameter, System.Delegate catchHandler) + 0x44 bytes 
  WindowsBase.dll!System.Windows.Threading.Dispatcher.InvokeImpl(System.Windows.Threading.DispatcherPriority priority, System.TimeSpan timeout, System.Delegate method, object args, bool isSingleParameter) + 0x91 bytes 
  WindowsBase.dll!System.Windows.Threading.Dispatcher.Invoke(System.Windows.Threading.DispatcherPriority priority, System.Delegate method, object arg) + 0x40 bytes 
  WindowsBase.dll!MS.Win32.HwndSubclass.SubclassWndProc(System.IntPtr hwnd = 464158, int msg = 49869, System.IntPtr wParam = 0, System.IntPtr lParam = 0) + 0xdc bytes 
  [Native to Managed Transition] 
  [Managed to Native Transition] 
  WindowsBase.dll!System.Windows.Threading.Dispatcher.PushFrameImpl(System.Windows.Threading.DispatcherFrame frame = {System.Windows.Threading.DispatcherFrame}) + 0xc7 bytes 
  WindowsBase.dll!System.Windows.Threading.Dispatcher.PushFrame(System.Windows.Threading.DispatcherFrame frame) + 0x49 bytes 
  WindowsBase.dll!System.Windows.Threading.Dispatcher.Run() + 0x4c bytes 
  PresentationFramework.dll!System.Windows.Application.RunDispatcher(object ignore) + 0x1e bytes 
  PresentationFramework.dll!System.Windows.Application.RunInternal(System.Windows.Window window) + 0x6f bytes 
  PresentationFramework.dll!System.Windows.Application.Run(System.Windows.Window window) + 0x26 bytes 
  PresentationFramework.dll!System.Windows.Application.Run() + 0x19 bytes 

Dan指出,指定两个ExportAttributes可能会从不同的ImportAttributes创建两个实例,但我相信它只创建了一个实例,因为我在Candy的构造函数中放了一个断点,它只在应用程序的生命周期中被点击。

1 个答案:

答案 0 :(得分:5)

在单个类上有多个导出是MEF中完全正常的用例。我们一直这样做没有问题。

丹的言论不正确。除非您在导入或导出中明确设置part creation policy,否则MEF将优先重用容器中的实例而不是创建多个实例。

您看到的COM异常与MEF无关,因为MEF本身是纯粹管理的,不使用任何COM互操作。您应该查看COM异常消息及其堆栈跟踪。要在发生此类异常时使调试器中断,请相应地配置visual studio(使用快捷键 ctrl + d e 查看相关设置)。

您描述的不稳定的调试器行为通常意味着源代码和编译的程序集不匹配。尝试清理bin文件夹,并检查项目之间的依赖关系是项目引用而不是直接程序集引用。如果依赖项的源代码已更改,则直接程序集引用不会正确触发重建。