获取现有导出值/对象(不是获取或创建)的MEF方法是什么?

时间:2012-03-18 05:38:26

标签: c#-4.0 mef

以下是我希望它如何运作

container.GetButDoNotCreate<T>(); // should throw (or can return null) if container doesn't contain an instance matching the contract

var x = container.GetExportedValue<T>();

var y = container.GetButDoNotCreate<T>(); // should return the object created in previous step

Assert.That(x).IsSameAs(y);

不同之处在于,如果容器不包含实例,则此方法不会创建实例。它是一个纯粹的get,如果它存在于容器中,请将此对象告诉我。 我需要它用于我的测试,我不希望测试代码在生产容器中创建对象(如果它们没有被创建)只使用现有的对象。只有生产代码才能在容器中添加/删除对象。

发布到MEF codeplex forum但没有回复。所以希望SO上的某个人可能有答案。 此外,如果我需要将该函数编写为扩展方法......那也可以作为答案。

2 个答案:

答案 0 :(得分:1)

这是我的解决方案。起初我尝试创建一个扩展方法。我尝试了几件事,阅读了文档并探讨了容器和目录上的可用属性,事件和方法,但我无法做任何事情。

在考虑问题后,我能想出解决方案的唯一方法是创建一个基于CompositionContainer的派生容器,并实现GetButDoNotCreate方法。

更新:我发布后发现该解决方案仅适用于您发布的简单示例,其中仅使用GetExportedValue检索零件简单零件。除非您将容器用作没有[Import]的零件的简单服务定位器,否则在创建具有[Import]的零件时将无法执行。

以下是实施:

using System;
using System.Collections.Generic;
using System.ComponentModel.Composition.Hosting;
using System.ComponentModel.Composition.Primitives;

namespace GetButNotCreate
{
    public class CustomContainer : CompositionContainer
    {
        private List<Type> composedTypes = new List<Type>();

        public CustomContainer(ComposablePartCatalog catalog)
            : base(catalog) 
        {
        }

        public new T GetExportedValue<T>()
        {
            if (!composedTypes.Contains(typeof(T)))
                composedTypes.Add(typeof(T));

            return base.GetExportedValue<T>();
        }

        public T GetButDoNotCreate<T>()
        {
            if (composedTypes.Contains(typeof(T)))
            {
                return base.GetExportedValue<T>();
            }

            throw new Exception("Type has not been composed yet.");
        }
    }
}

它的工作原理是覆盖GetExportedValue方法以跟踪到目前为止已组成的类型,然后使用它来检查GetButNotCreate中的类型组合。我抛出了你问题中提到的异常。

当然,您可能需要覆盖GetExportedValue的重载(除非您不使用它们,但即便如此,我仍会为了安全而覆盖它们)并且如果您使用该类,可能会添加其他构造函数和内容。在这个例子中,我做了最低限度的工作。

以下是测试新方法的单元测试:

using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;

namespace GetButNotCreate
{
    public interface IInterface { }

    [Export(typeof(IInterface))]
    public class MyClass : IInterface 
    {
    }

    [TestClass]
    public class UnitTest1
    {      
        [TestMethod]
        [ExpectedException(typeof(Exception), "Type has not been composed yet.")]
        public void GetButNotCreate_will_throw_exception_if_type_not_composed_yet()
        {
            var catalog = new AssemblyCatalog(typeof(UnitTest1).Assembly);
            CustomContainer container = new CustomContainer(catalog);
            container.ComposeParts(this);

            var test = container.GetButDoNotCreate<IInterface>();
        }

        [TestMethod]
        public void GetButNotCreate_will_return_type_if_it_as_been_composed()
        {
            var catalog = new AssemblyCatalog(typeof(UnitTest1).Assembly);
            CustomContainer container = new CustomContainer(catalog);
            container.ComposeParts(this);

            var x = container.GetExportedValue<IInterface>();
            var y = container.GetButDoNotCreate<IInterface>();

            Assert.IsNotNull(y);
            Assert.AreEqual(x, y);
        }
    }
}

它表明如果从未导出类型,GetButNotCreate将抛出异常,如果已导入该类型,它将返回此类型。

我无法在任何地方找到任何钩子(不使用反射)来查看MEF是否已经组成了一个部件,所以这个CustomContainer解决方案将是我最好的选择。

答案 1 :(得分:0)

我认为值得在Container上拥有自己的包装器。像IContainerWrapper之类的东西,并在代码中的任何地方使用它。

实现这一目标:

  • 如果您有通常的单元测试,您只需注入另一个适合您需要的包装实例。

  • 如果要访问生产容器,但具有上述行为,则可以在生产包装器实现中使用预处理器指令。