【问题标题】:What is the MEF method to get an existing exported value / object (not get Or Create)?获取现有导出值/对象(不是获取或创建)的 MEF 方法是什么?
【发布时间】:2012-03-18 05:38:25
【问题描述】:

这就是我希望它的工作方式

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

不同之处在于,如果容器不包含该实例,则此方法不会创建实例。这是一个纯粹的获取,即如果它存在于容器中,就给我这个对象。 我的测试需要它,我不希望测试代码在生产容器中创建对象(如果它们没有创建),只需使用现有的。只有生产代码应该向容器添加/删除对象。

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

【问题讨论】:

  • 我会质疑为什么您似乎在单元测试中测试容器?
  • @MatthewAbbott - 我正在尝试在 ViewModel 层编写系统测试(使用 ViewModel 优先设计构建)。这些不是单元测试......我不想操纵 UI 元素,而是想进入容器,抓取视图模型并达到相同的效果。
  • 您是否考虑过为容器编写自己的包装器并在任何地方使用它的接口。但是对于单元测试,只需注入该包装器的另一个实例?

标签: c#-4.0 mef


【解决方案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 解决方案将是我最好的选择。

【讨论】:

  • @Giles - 是的,这种方法需要我知道可以构造对象的所有各种 MEF 路径并拦截这些路径。此外,如果构建对象树,这将只跟踪根。其次,我不确定这个中间人是否会阻止零件的垃圾收集。
  • 不过 +1 表示尝试/努力。我现在质疑这种需求.. 即使 ViewModel 是从测试(而不是生产代码)创建的(在 getExportedValue 上),测试也会失败,因为它可能不会被初始化/连接到其他协作者。跨度>
  • @Gishu 在发布我的初始帖子后,我尝试使用事件,我还尝试了另一种使用扩展方法的解决方案,但我真的想不出任何东西。也许使用反射,您可以创建一个扩展方法,查看 MEF 正在为其部件使用的内部集合,并查看该部件是否已经存在于集合中。
【解决方案2】:

我认为在 Container 上拥有自己的包装器是值得的。像 IContainerWrapper 这样的东西,并在您的代码中随处使用它。

有了这个:

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-11-17
    • 1970-01-01
    • 1970-01-01
    • 2017-06-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多