2010-04-20 10 views
3

J'ai une situation intéressante où je dois faire quelque chose comme ceci:Plusieurs exportations avec MEF fait des choses vraiment odieuses - pourquoi, et pourquoi est-ce permis?

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

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

je ne pouvais pas trouver des messages partout en ce qui concerne l'utilisation de plusieurs [Exporter] attributs, alors je me suis , que diable, pourrait aussi bien essayer.

À première vue, cela semblait fonctionner. J'ai quelques méthodes qui appellent dans les deux interfaces d'une instance de Candy, et c'était bien.

Cependant, comme j'ai commencé à tester l'application, j'ai vu que le comportement n'était pas correct, et en regardant la fenêtre de sortie, j'ai vu que j'obtenais tonnes de COMExceptions. Je ne pouvais pas savoir d'où ils venaient tous, mais ils se produisaient toujours lorsqu'un thread de travail dormait. Je me suis dit que ça devait venir du fil principal, mais je ne savais pas comment déboguer ça. Rien n'aurait dû se passer dans l'interface graphique, et j'ai désactivé mes DispatchTimers juste au cas où - même chose.

Encore plus étrange que les COMExceptions était le comportement vraiment, vraiment erratique en passant par le code. Environ 30% du temps, lorsque je devais faire un pas, il sortait de la méthode, ou il faisait un pas sur deux lignes de code! Des trucs totalement bizarres que je n'ai pas l'habitude de voir.

La seule chose qui a changé entre le code de travail et non-travail a été l'introduction de MEF à travers mon code de chargement de plugin. Donc, comme un test, j'ai changé mon assembly de plugin pour exporter seulement une interface, et j'ai tout codé en dur dans l'application qui reposait sur l'autre (maintenant non-implémenté) interface. Et maintenant les COMExceptions ont disparu, et le comportement de débogage bizarre est parti.

Est-ce quelque chose que les gens ici ont déjà vu? Si MEF ne devrait pas permettre à une classe d'exporter plusieurs interfaces, une exception CompositionException ne devrait-elle pas être levée lors de la composition des parties? Quelqu'un peut-il expliquer pourquoi MEF causerait ces problèmes étranges ???

Voici un exemple de la pile d'appels du thread principal au moment de l'exception COMException. Je ne sais pas si cela signifie quelque chose pour tout le monde, mais si vous pouvez suggérer des moyens de déboguer cela, ce serait génial.

> 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 a fait remarquer que la spécification deux ExportAttributes serait probablement créer deux instances des différents ImportAttributes, mais je crois qu'il ne crée une instance parce que je mets un point d'arrêt dans le constructeur pour Candy et il n'a été touché pendant la durée de vie de l'application.

Répondre

5

Avoir plusieurs exportations sur une seule classe est un cas d'utilisation parfaitement normal dans MEF. Nous faisons cela tout le temps sans problèmes.

La remarque de Dan n'est pas correcte. Si vous ne définissez pas explicitement un part creation policy à l'importation ou à l'exportation, MEF préférera réutiliser des instances dans un conteneur plutôt que de créer plusieurs instances. Les exceptions COM que vous voyez n'ont rien à voir avec MEF, car MEF lui-même est purement géré et n'utilise pas d'interopérabilité COM. Vous devriez jeter un coup d'oeil au message d'exception COM et à sa trace de pile.Pour que le débogueur se casse lorsqu'une telle exception se produit, configurez Visual Studio en conséquence (Utilisez le raccourci ctrl + d, et pour voir les paramètres appropriés). Le comportement du débogueur erratique que vous décrivez indique généralement que le code source et l'assembly compilé ne correspondent pas. Essayez de nettoyer votre dossier bin et vérifiez que les dépendances entre vos projets sont des références de projet plutôt que des références d'assemblage direct. La référence d'assembly direct ne déclenche pas correctement les reconstructions si le code source de la dépendance a changé.

+0

Je suis heureux d'entendre que cela n'a rien à voir avec MEF. La chose étrange est que maintenant je suis à la maison travaillant sur le même code (j'ai marqué la version qui a lancé COMExceptions), et il n'a pas le même comportement * du tout *. Je vais réexécuter le code au travail et essayer d'obtenir une pile d'appel demain matin. Merci de tout éclaircir avec la politique de création de pièces. – Dave

+0

@Wim: Oui, j'utilise CTRL-D, E pour quand j'ai vraiment besoin de creuser des exceptions. C'est exactement ce que j'ai fait au bureau quand j'obtenais des Exceptions COM, mais c'est le premier cas où quand l'exception est levée, elle n'a pas ** ** passé au thread au moment où l'exception a été lancée !!! Vraiment étrange. Je vais faire un nettoyage complet demain. Hmm .. en fait, je vais juste RDP maintenant et voir ce qui se passe. – Dave

+0

@Wim: avez-vous d'autres suggestions? J'ai nettoyé, reconstruit, testé ceci sur plusieurs systèmes, et cette exception COMException ** seulement ** arrive sur une de mes machines! Je ne peux pas obtenir le code pour arrêter l'exécution au moment où le COMException est levé, indépendamment de mes options VS2008. – Dave