Ignore constructor exceptions with MEF without Visual Studio breaking at the exception
Asked Answered
S

4

7

I would like to create some existing code-modules (IMyDesiredType) to load with MEF. The modules mostly have some constructor arguments which I want to provide with MEF (ImportingConstructor). So far this works fine.

The problem now arises because sometimes the dependencies are not available (they are null) in the host application. The modules will throw by convention an ArgumentNullException and I don’t want to change that. However I want MEF to overlook such objects (not include them in the object-graph).

 [Export(typeof(IMyDesiredType))]
 class MyModule : IMyDesiredType{
    [ImportingConstructor]
    public MyModule(object aNecessaryDependency){
       if(aNecessaryDependency==null) throw new ArgumentNullException(nameof(aNecessaryDependency))
     }
 }

To acquire this, I let MEF create Lazy<IMyDesiredType> instances and the initializing them one by one.

foreach(var myLazy in collectionOfMefExports){
  try{
      myLazy.Value // do something with the value, so the object gets composed
   }catch(CompositionException){
      // Here I get the ArgumentNullException wrapped in a CompositionException
      // and also can work around it. However because of the exception handling
      // is on the first hand in MEF, VS will break always in the throwing 
      // constructor of the module
      continue; // Go to the next module after logging etc.
   }
} 

The problem here is, that I have to catch CompositionException and not directly the Exception (mostly ArgumentNullException) from the module's constructor. Therefore, Visual-Studio breaks on each module, because of the Exception is not catched from user-code. The obvious solution to this is, to tell visual studio not to break on ArgumentNullException-types, but this feels very “hackish” to me. And in any other place, I want VS to break on ArgumentNullExceptions.

Is there another pattern with which I can make MEF not to add components to the graph, where the dependency is declared ([Export]), but it’s value is null, or is there a method of a MEF-class I can override in a derived class and catch the constructor-exception on the fore-hand?

Please leave a comment, if the question is not clear, I'm not a native english speaker and therefore maybe the quesion is verbalized a litte confusing.

Senegal answered 1/3, 2017 at 10:46 Comment(8)
Did you already try [DebuggerHidden] and [DebuggerStepThrough] attributes as the answers there suggests? #1420890Magdeburg
Thanks a lot for your suggestion. Sadly it works the other way round: I would have to decorate each constructor of the exporting modules, what however makes them to not throw these exceptions in all runtime environments and not only in the one where I use them as simple plugins via MEF.Senegal
is the question to prevent " Visual-Studio breaks on each module, ..." ?Deictic
@Julian: If I understand your question right: yes, but only while they were instantiated by MEF.Senegal
@grek40: please make an answer out of your comment. If nothing more specific comes around in the next days, I will accept it as the answer. It comes close to what I desire.Senegal
You stated sometimes the dependencies are not available. May i ask why they are not availiable? Are those dependencies loaded on a later point of time? This seems to me the real issue here. Handling exceptions is never as good as avoiding them imoColmar
@lokusking: The part wehre MEF comes into play is a little WPF utility which allows the user to select a specific environment (database etc) and select and execute one of the described modules. Not every module has the same dependencies (normally they are set-up via other IOC-containers (mostly windsor), but in this specific project they are used as plugins (via MEF). The goal is (which is easy achievable, but with the described hiccup), to show only the modules which have all dependencies satisfied (marked in the WPF-app as Exportable and their current value is not null).Senegal
@likusking: Part 2: Therefore the absence of the value is inavoidable, because it's based on the user's selection in the WPF application.Senegal
M
1

Unfortunately, the support for what you ask for is somehow limited.

Visual Studio allows to configure by exception type, whether the debugger should break. This doesn't really help when you try to show/hide the same type of exception based on the execution context. Still you could derive your own exception type and use it in the importing constructors. This would allow you to configure breaks by exception type, but would not make a difference between MEF composition and other code.

Also, methods can be marked to be ignored by the debugger. See this related answer Don't stop debugger at THAT exception when it's thrown and caught.

Disregarding [DebuggerStepThrough] since it is reported to be unreliable, option one is [DebuggerHidden]. I want to add another candidate: [DebuggerNonUserCode]. This plays with the VS option "Enable just my code" (see http://blog.functionalfun.net/2008/05/debuggernonusercode-suppressing.html).

So while [DebuggerHidden] will never break for the exception in the constructor where it is thrown and will instead report it in the next surrounding user code, [DebuggerNonUserCode] allows you to ignore or break in the constructor depending on your VS debugging settings. As long as "Enable just my code" is set, both attributes should behave the same way.

Supposed the MEF initialization is fully handled in debugger-hidden code, for non-MEF constructor calls, the debugger will break when it first reaches a surrounding function that is not marked as hidden.

Magdeburg answered 9/3, 2017 at 23:56 Comment(1)
Not what I hoped for but the closest to the problem. Thx.Senegal
B
0

It sounds like only you know which components are important and which aren't. you likely want to do something like wrap the component initialisation in a try catch like you already are and through a configuration lookup decide if this particular exception is problematic or not in code.

Basically you need to build the rules yourself, but either way a failed component load is a failure so the components that throw exceptions can't be added to the graph properly I would think.

The commonly accepted approach seems to be to remove the failure component then recompose the graph ...

How do I get MEF to recompose when I change a part?

...

this may help you though in diagnosing how / what to do next ..

https://blogs.msdn.microsoft.com/dsplaisted/2010/07/13/how-to-debug-and-diagnose-mef-failures/

Boots answered 7/3, 2017 at 16:57 Comment(3)
Thx, I will look deeper into it tomorrow. However I suspect, that it does not help resolve the issue, I'm already able to not have the plugin activated, this is not my problem. The problem (or the hiccup to be treated) is, that the exception happens in the constructor of the module and therefore Visual Studio will break in every constructor unless I disable the breaking behavior for the concerned exceptions in Visual Studio or with the [DebuggerHidden]/[DebuggerStepThroug]-attributes (thx grek40). I think the only way for a proper solution is to subclass MEF-classes.Senegal
from that description it sounds like the issue you have is that you don't want VS to stop on "some exceptions" when you attach it to your code?Boots
FYI there is no way to get VS to "selectively ignore some specific exceptions", but you can tell it to ignore exceptions by type (as you have implied you already seem to know)Boots
H
0

I may be heading the wrong direction, but does an abstract factory with default implementation for unhandled cases help?

I use this for my dependency with unity DI... Interesting question no doubt!

<Export(GetType(IComponent))>
Public Class DependencyResolver
    Implements IComponent
    Public Sub SetUp(registerComponent As IRegisterComponent) Implements IComponent.SetUp
        'General
        registerComponent.RegisterType(Of IDataContextAsync, dbEcommEntities)()  

        'DomainLogic
        registerComponent.RegisterType(Of IUserDomainLogic, MfrUserDomainLogic)()

        'Services
        registerComponent.RegisterType(Of ICompanyService, CompanyService)()

    End Sub

End Class

Injecting constructors -- I have my MEF export in another class library with its own dependency resolver. In each web application I then register the components for the new application. Each part I received from the MEF export dependency resolver is then able to be extended by injecting the constructors of my mvc controllers. In the Component loader I then load up the container with the dll and also able to extend the dependencies by adding a unity.config after the fact.

Private Shared Sub RegisterDependencies(container As IUnityContainer)
        'load services 
        ComponentLoader.LoadContainer(container, ".\\bin", "Service.dll")

        'load config
        'container.LoadConfiguration()
    End Sub
Horsy answered 9/3, 2017 at 20:8 Comment(1)
Hi thanks for your answer. I also thought about this, however if I would to this on the caller side, it would only postpone the problem and if I would do it on the module site, I would have to write a factory for every module (because of the varying constructor parameters), and this is unwanted. Or have I misunderstood your proposition?Senegal
C
0

The MEF had already included this as a feature since the MEF Preview 6 and It's called the stable composition. The MEF safely get booted even if the dependencies are not supplied.

Simply a solution to you problem would be to check if the Value is actually created before accessing the value.

if(myLazy.IsValueCreated)
    myLazy.Value // do something with the value, so the object gets composed

You can read more about it here.

Note: If you have optional dependencies and system can work without them then do not put Argument null check in constructor. It will make them Required dependency during the composition.

As you already mentioned, you don't want to change the convention of throwing exception when argument is null. Well if you know that system would work without those dependencies then you can avoid placing that check for such dependencies.

Cavity answered 9/3, 2017 at 21:1 Comment(3)
Hi thanks for your answer. It sounds promising. However I have not understand it fully: Lazy{T} makes the module load at a later time. If I would check IsValueCreated, it would always be false as long as I don’t have accessed the value (this seen to me the sole purpose of Lazy{}) and also MEF will not try to instantiate the real object because of the lazy{T}. And if I call Value, I’m at the same point as befored. But maybe I miss the point? Can you elaborate further?Senegal
As for for the note-part of your answer: The dependencies are not optional. The modules are existing and they require the constructor parameters. Therefore, the ArgumentNullException. The best thing would be, if I could tell MEF to not try to create dependencies, if an importing constructor desires a dependency which is exportable but it’s value is currently null. However I have not found such a possibility.Senegal
I think I wrote it too fast.. you're right Lazy won't do what I expected it would. I think we need mefx library classes for Diagnostics. I will update my answer may be on weekend to include more details.Cavity

© 2022 - 2024 — McMap. All rights reserved.