Question

I've a requirement where I've to support two types of container in my application. Therefore I've created a custom ServiceLocator that serves 2 types of containers i.e.

  1. SimpleIoc (Galasoft)

  2. MefServiceLocatorAdapter

Therefore I've created a common service locator class i.e.

public class CommonServiceLocator : ServiceLocatorImplBase
{
    private IServiceLocator _iocContainer, _mefContainer;

    public CommonServiceLocator(IServiceLocator iocLocator, IServiceLocator mefLocator)
    {
        _iocContainer = iocLocator;
        _mefContainer = mefLocator;
    }

    protected override IEnumerable<object> DoGetAllInstances(Type serviceType)
    {

        IEnumerable<object> services;
        try
        {
            services = _iocContainer.GetAllInstances(serviceType);
        }
        catch (Exception)
        {
            //Current assumption is that if IocContainer doesn't contain an instance then
            //it should look for MefContainer for values
            services = _mefContainer.GetAllInstances(serviceType);
        }
        return services;
    }

    protected override object DoGetInstance(Type serviceType, string key)
    {
        object service;
        try
        {
            service = _iocContainer.GetInstance(serviceType, key);
        }
        catch (Exception)
        {
            //Current assumption is that if IocContainer doesn't contain an instance then
            //it should look for MefContainer for values
            service = _mefContainer.GetInstance(serviceType, key); //<== This fails to get an instance
        }
        return service;
    }

    public override object GetInstance(Type serviceType, string key)
    {
        return DoGetInstance(serviceType, key);
    }

    public override object GetInstance(Type serviceType)
    {
        return GetInstance(serviceType, null);
    }
}

Now, the problem is when I try to get a class instance through IOC container using GetInstance() method it works fine. However, obtaining a class instance through MefContainer throws some error ("A first chance exception of type 'System.InvalidOperationException' occurred in System.Core.dll"). I've created a test class to test this scenario:

public class ServiceLocatorTest
{
    public void CommonServiceLocatorInstanceTest()
    {
        var serviceLocator = new SimpleIoc();
        serviceLocator.Register<GalaSoftIocData>();

        AggregateCatalog catalog = new AggregateCatalog();
        catalog.Catalogs.Add(new AssemblyCatalog(typeof(MefCompositionAddin).Assembly));
        CompositionContainer compContainer = new CompositionContainer(catalog);

        CompositionBatch batch = new CompositionBatch();

        batch.AddPart(AttributedModelServices.CreatePart(new MefCompositionAddin()));

        compContainer.Compose(batch);

        var vl = compContainer.GetExportedValue<MefCompositionAddin>(); // <== This gives instance correctly
        var mefserviceLocator = new MefServiceLocatorAdapter(compContainer);
        var commonServiceLocator = new CommonServiceLocator(serviceLocator, mefserviceLocator);
        var galaSoftData = commonServiceLocator.GetInstance(typeof(GalaSoftIocData));
        var mefData = commonServiceLocator.GetInstance(typeof(MefCompositionAddin));
    }
}

[Export]
class MefCompositionAddin
{

    public string MyData { get { return "Mef's Data composed"; } }

    [Import]
    public MefCompositionAddin MyObj { get; set; }
}

class MefCompositionData
{
    [Import]
    public MefCompositionAddin MyAddin { get; set; }
}

class GalaSoftIocData
{
    public string MyData { get { return "Galasoft's Data composed"; } }
}

Kindly let me know if i've missed anything. and what could be the possible cause. Thanks everyone for your help in advance.

Was it helpful?

Solution

I believe that you are using the MefServiceLocatorAdapter provided in the Prism library. This is the code for its DoGetInstance method:

protected override object DoGetInstance(Type serviceType, string key)
{
    IEnumerable<Lazy<object, object>> exports = this.compositionContainer.GetExports(serviceType, null, key);
    if ((exports != null) && (exports.Count() > 0))
    {
        // If there is more than one value, this will throw an InvalidOperationException, 
        // which will be wrapped by the base class as an ActivationException.
        return exports.Single().Value;
    }

    throw new ActivationException(
        this.FormatActivationExceptionMessage(new CompositionException("Export not found"), serviceType, key));
}

As you can see in the comment, if you have more than one type exported for the corresponding service type and key it will thrown the exception you are describing due to the Single method.

You can either review the exports in you app to see if there is some type mapped to multiple classes (or the same class exported multiple times,) or rewrite that method so that it doesn't throw an exception (for example, using FirstOrDefault instead of Single.)

Note: I did not add the aforementioned comment, it is included in the library's code.

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