定制服务定位器,带有来自galasoft的MEF和IOC容器

时间:2013-12-23 06:50:13

标签: c# .net prism mef

我有一个要求,我要在我的应用程序中支持两种类型的容器。因此,我创建了一个定制的ServiceLocator,它提供两种类型的容器,即

  1. SimpleIoc(Galasoft)

  2. MefServiceLocatorAdapter

  3. 因此我创建了一个公共服务定位器类,即

    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);
        }
    }
    

    现在,问题是当我尝试使用GetInstance()方法通过IOC容器获取类实例时,它可以正常工作。但是,通过MefContainer获取类实例会引发一些错误(“ System.Core.dll 中发生的'System.InvalidOperationException'类型的第一次机会异常”)。我已经创建了一个测试类来测试这个场景:

    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"; } }
    }
    

    如果我错过了什么,请告诉我。可能的原因是什么?感谢大家提前帮助。

1 个答案:

答案 0 :(得分:2)

我相信您使用的是 Prism 库中提供的 MefServiceLocatorAdapter 。这是 DoGetInstance 方法的代码:

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));
}

正如您在评论中看到的,如果您为相应的服务类型和密钥导出了多个类型,则由于单个方法,它将抛出您描述的异常。

您可以查看应用中的导出,以查看是否有某些类型映射到多个类(或多次导出的同一个类),或者重写该方法以使其不会抛出异常(例如,使用 FirstOrDefault 代替单个。)

注意:我没有添加上述注释,它包含在库的代码中。