简体   繁体   English

如何替换MEF容器中的导出部件/对象?

[英]How to replace an exported part / object in a MEF container?

I have a WPF app running, which needs all operations that affect the UI to be on the UI Thread. 我有一个WPF应用程序正在运行,它需要影响UI的所有操作都在UI线程上。 WPF also provides a Dispatcher class that handles this - so I extracted that into a dependency. WPF还提供了一个Dispatcher类来处理这个 - 所以我把它提取到一个依赖项中。

public interface UIActionExecutor
    {
        void Do(Action action);
    }

So in my production code, I use an exported implementation which delegates to the WPF Dispatcher. 因此,在我的生产代码中,我使用了一个委托给WPF Dispatcher的导出实现。 I'm using MEF for DI. 我正在使用MEF进行DI。

Now the problem, in my acceptance tests, I need to replace the part / object in the container that responds to UIActionExecutor by a Mock. 现在问题是,在我的验收测试中,我需要替换容器中的部件/对象,该部件/对象由Mock响应UIActionExecutor So I need to remove ExecutorUsingWpfDispatcher from my container and add MockUIActionExecutor in its place. 所以我需要从容器中删除ExecutorUsingWpfDispatcher并在其位置添加MockUIActionExecutor This sounds pretty simple (if I was not using MEF)... but my searching skills haven't helped me find an answer as to how to do this with the MEF container ? 这听起来很简单(如果我没有使用MEF)......但我的搜索技巧并没有帮助我找到关于如何使用MEF容器做到这一点的答案?

Update: If anyone wants to know why/how the solution works - read Glenn Block's blog post#2 . 更新:如果有人想知道解决方案为何如何/如何运作 - 请阅读Glenn Block的博客文章#2 This is what I ended up using 这就是我最终使用的

    var defaultExportProvider = new CatalogExportProvider(__defaultCatalog);
    var catalogOfMocks = new AssemblyCatalog(assemblyExportingMocks);
    // order of params important (precedence left to right)
    __container = new CompositionContainer(catalogOfMocks, defaultExportProvider);
    defaultExportProvider.SourceProvider = __container

A DI container is responsible for wiring everything together. DI容器负责将所有部件连接在一起。

A unit test is responsible for testing a single unit of code in isolation. 单元测试负责单独测试单个代码单元。 Mocks are used to replace dependencies. 模拟用于替换依赖项。 So in principle a DI container should not be used in a unit test. 所以原则上DI容器不应该用于单元测试。 It contradicts the definition of "unit test".(¹) 它与“单元测试”的定义相矛盾。(¹)

However, I can certainly understand that you might want to do automated integration tests in addition to unit tests, and you might want to use MEF yet replace certain MEF parts in such a test. 但是,我当然可以理解,除了单元测试之外,您可能还希望进行自动集成测试,并且您可能希望在此类测试中使用MEF但更换某些MEF部件。 You can do that like this: 你可以这样做:

// first set up the main export provider
var mainCatalog = new AggregateCatalog(...); 
var mainExportProvider = new CatalogExportProvider(mainCatalog);

// now set up a container with mocks
var mockContainer = new CompositionContainer();
mockContainer.ComposeExportedValue<IFoo>(fooMock);
mockContainer.ComposeExportedValue<IBar>(barMock);

// use this testContainer, it will give precedence to mocks when available
var testContainer = new CompositionContainer(mockContainer, mainExportProvider);

// link back to the testContainer so mainExportProvider picks up the mocks
mainExportProvider.SourceProvider = testContainer;

(¹) Judging from your blog, I'm sure you already know this. (¹) 从你的博客来看,我相信你已经知道了。 But others will also read this answer, so I wanted to be clear about the term "unit test". 但是其他人也会读到这个答案,所以我想清楚“单元测试”一词。

Could not get the accepted solution to work. 无法获得已接受的解决方案。 Below code should work and precedence is described in the documentation for AggregateExportProvider . 下面的代码应该工作,优先级在AggregateExportProvider的文档中描述。

var mainContainer = new CompostionContainer();
mainContainer.ComposeExportedValue<IFoo>(new Foo() {Test = 1});

var mockContainer = new CompositionContainer();            
mockContainer.ComposeExportedValue<IFoo>(new Foo() {Test = 2});

var aggregateExportProvider = new AggregateExportProvider(
     mockContainer,   // IFoo in this container takes precedence
     mainContainer);

IFoo foo = aggregateExportProvider.GetExportedValue<IFoo>();

Console.WriteLine(foo.Test); // Outputs: 2

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM