Question

I have a Caliburn Micro's bootstrapper and I use the MEF as IoC. One of interfaces implementors can throw exception from it's constructor. So, when I do the following:

 CompositionBatch batch = new CompositionBatch();
 batch.AddExportedValue<IFrProvider>(new ShtrihFr());

Then I get exception at application startup, but I want to get it at the time of resolvation. How to accomplish that with MEF?

Update 1.

Here what I did:

[Export(typeof (LoadingViewModel))]
public class LoadingViewModel {    
    public LoadingViewModel() {}

    [Import]
    private readonly IFrProvider frProvider;
 }

[Export(typeof(IFrProvider))]
public class ShtrihFr : IFrProvider {
    [ImportingConstructor]
    public ShtrihFr(int password = 1) {          
    }
}

So when I do the following:

 protected override object GetInstance(Type serviceType, string key) {
        string contract = string.IsNullOrEmpty(key) ? AttributedModelServices.GetContractName(serviceType) : key;
        IEnumerable<object> exports = container.GetExportedValues<object>(contract);

        var exportedList = exports as IList<object> ?? exports.ToList();
        if (exportedList.Any())
            return exportedList.First();

        throw new Exception(string.Format("Could not locate any instances of contract {0}.", contract));
    }

exportedList count is 0. It can't find the implementation. I cheked that the container has amidts it's parts the ShtrihFr implementation. How to solve the problem?

Update 2. For debugging purpose I did added the following to the beggining of GetInstance method:

if (serviceType.FullName == "Microtech.Hardware.IFrProvider") {
            var export = container.GetExport<IFrProvider>();
            var frProvider = export.Value;
}

At line container.GetExport I get ImportCardinalityMismatchException. No exports were found that match the constraint...

Was it helpful?

Solution

One way to achieve this is the following. On application startup create Catalog that contains all composable parts of your application and initialize the CompositionContainer. Here is the code.

var catalog = new DirectoryCatalog(path to the directory that contains your dlls, "*.dll");
var compositionContainer = new CompositionContainer(catalog);
compositionContainer.ComposeParts();

Next mark your ShtrihFr class with Export attribute:

[Export(typeof(IFrProvider))]
public class ShtrihFr : IFrProvider
{
    public ShtrihFr()
    {
        throw new NotImplementedException("Not Implemented");
    }
}

This way the CompositionContainer through the DirectoryCatalog will only get list of composable parts and their contracts. Actual instances will not be created, so you won't get exception on application startup.

When you need instance of the ShtrihFr class, you can use one of the following lines:

var part = compositionContainer.GetExportedValue<IFrProvider>();

The above line will throw exception if the constructor throws exception, which is the case now.

var part = compositionContainer.GetExport<IFrProvider>();

The above line will not throw exception when executed, but to get the actual instance you will need to access the Value property of the part variable, which will throw exception.

Update 1:

Add default constructor to the ShtrihFr class, so it look like this:

[Export(typeof(IFrProvider))]
public class ShtrihFr : IFrProvider 
{
    public ShtrihFr(int password = 1) 
    {          
    }

    [ImportingConstructor]
    public ShtrihFr(int password = 1) 
    {          
    }
}

And instead of using the overridden GetInstance method, use something like this, if you need collection of exported parts:

public IEnumerable<T> GetInstances<T>(Type constraint = null)
{
    if (constraint == null)
        return compositionContainer.GetExportedValues<T>();

    return compositionContainer.GetExportedValues<T>(AttributedModelServices.GetContractName(constraint));
}

or something like this, if you need single exported part:

public T GetInstance<T>(Type constraint = null)
{
    if (constraint == null)
        return compositionContainer.GetExportedValue<T>();

    return compositionContainer.GetExportedValue<T>(AttributedModelServices.GetContractName(constraint));
}

In both methods, the type parametar T is the type of the exported part you want to instantiate. The constraint parameter passed to both methods is additional constraint that is optional and if needed is applied to the Export attribute of the classes.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top