Многократный экспорт с помощью MEF делает действительно ужасные вещи - почему и почему это разрешено? - PullRequest
3 голосов
/ 20 апреля 2010

У меня интересная ситуация, когда мне нужно сделать что-то вроде этого:

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

где

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

Я нигде не смог найти ни одного сообщения, касающегося использования нескольких атрибутов [Export], поэтому я подумал, что, черт возьми, может с таким же успехом попробовать.

На первый взгляд, похоже, что это действительно работает. У меня есть пара методов, которые вызывают оба интерфейса экземпляра 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 

Дэн указал, что указание двух ExportAttributes, вероятно, создаст два экземпляра из разных ImportAttributes, но я считаю, что он создал только один экземпляр, потому что я установил точку останова в конструкторе для Candy, и она была достигнута только во время жизни приложения.

1 Ответ

5 голосов
/ 21 апреля 2010

Наличие нескольких экспортов для одного класса - вполне нормальный вариант использования в MEF.Мы делаем это все время без проблем.

Замечание Дэна неверно.Если вы явно не зададите политику создания детали при импорте или экспорте, MEF предпочтет повторно использовать экземпляры внутри контейнера, а не создавать несколько.

Исключения COM, которые вы видите, не имеют никакого отношенияделать с MEF, так как сам MEF является чисто управляемым и не использует использование взаимодействия COM.Вы должны взглянуть на сообщение об исключении COM и его трассировку стека.Чтобы отключить отладчик при возникновении такого исключения, настройте Visual Studio соответствующим образом (используйте ярлыки ctrl + d , e для просмотра соответствующих настроек).

Неустойчивое поведение отладчика, которое вы описываете, обычно означает, что исходный код и скомпилированная сборка не совпадают.Попробуйте очистить папку bin и убедитесь, что зависимости между вашими проектами являются ссылками на проекты, а не прямыми ссылками на сборки.Прямая ссылка на сборку не вызывает корректного перестроения, если исходный код зависимости изменился.

...