Reputation: 36785
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 ArgumentNullException
s.
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.
Upvotes: 7
Views: 715
Reputation:
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
Upvotes: 0
Reputation: 13458
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.
Upvotes: 1
Reputation: 14687
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.
Upvotes: 0
Reputation: 8618
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/
Upvotes: 0