8

我们使用的是 .NET 4.5,并且在 MEF 中打开通用导出没有问题,直到一个月前它突然停止工作。我们 CI 服务器上的 70 个测试变成红色,我们将其追溯到容器中缺少的部分。

我觉得很奇怪,所以我写了这个测试代码:

var catalog = new TypeCatalog(typeof(Impersonator<>), typeof(Cache<>));
var parts = catalog.Parts;

但看起来没有一个开放的泛型类型将在目录中注册。 Parts是一个TypeCatalogTypes = <Empty>

通常我们通过使用这个 SO question中的属性进行声明性导出来解决这个问题,但似乎没有任何替代方案可以工作了。

任何想法将不胜感激。

4

5 回答 5

2

我通过将条目 < httpRuntime targetFramework="4.5" /> 在我的 web.config 中解决了这个问题。对于非 Web 应用,请确保 app.config 具有条目 <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />。

有关详细信息,请参阅http://blogs.msdn.com/b/webdev/archive/2012/11/19/all-about-httpruntime-targetframework.aspx

于 2014-03-01T12:24:33.003 回答
2

知道是什么触发了它开始失败吗?您是否在该时间范围内获得了 .NET 更新?

出于好奇,如果您编写一个针对 .NET 4.5 的简单控制台应用程序,这个问题会重现吗?

我问的原因是我相信 .NET 4.5 发布了更新,以确保开放泛型不会破坏现有的 .NET 4.0 应用程序,因此如果由于某种原因您的测试在 .NET 4.0 模式下运行,那么开放泛型支持将被禁用,这可能会导致您所看到的。

于 2013-08-21T04:29:16.190 回答
1

我知道这是一个老问题,但我有一些额外的信息可以帮助其他人。

我遇到了同样的问题。我的单元测试正在工作,然后突然停止。我们将其缩小为一对 Windows 更新:KB2840642v2 和 KB2840642。卸载这些后,MEF 在单元测试中再次开始正常工作。我在发行说明中没有看到任何应该影响事情的更新,但你知道这是怎么回事。

但是,不幸的是,我现在正在运行 Windows 8,并且问题再次出现。我没有安装这些更新。我认为它们本机并入操作系统。

于 2014-02-11T20:46:56.753 回答
1

当您说“在我们的 CI 服务器上”时,这并不能告诉我们您是如何运行测试的。但是,我似乎在使用vstest.console.exe.

我发现 MEF 的自动打开类型关闭功能(.net-4.5 中的新功能)在使用 .net-4.5 运行测试时似乎会中断vstest.console.exe。基本上,vstest.console.exe可能决定在 .net-4.0 兼容模式下运行,就好像/Framework:Framework40已传递给它一样。.net-4.5 程序集即使在由 .net-4.0 运行时加载时也能正常加载和运行,但 MEF 检测到您在 .net-4.0 模式下运行并禁用其对自动关闭打开的泛型类型的支持。您可以通过传递开关强制vstest.console.exe在 .net-4.5 模式下运行,让 MEF 再次正常运行。/Framework:Framework45

(这在安装了 .net-4.6.1 的机器上进行了测试,但不确定这是否会改变更多)。

最小复制

当我针对 .net-4.5 框架制作一个简单的测试程序集时,vstest.console.exe正确地猜测它应该使用 .net-4.5 运行测试。但是,当我制作一个集成到更复杂的构建环境中的程序集时,vstest.console.exe突然开始以 .net-4.0 模式而不是 .net-4.5 模式运行。因此,我从复杂的构建环境开始,并开始缩减内容,直到问题消失。

vstest.console.exe猜测错误的框架,您需要两个程序集。一方面,定义一个自定义程序集Attribute。另一方面,应用该自定义程序集属性并定义您的单元测试。

第一次组装:

using System;

[AttributeUsage(AttributeTargets.Assembly)]
public class BreakVSTestFrameworkDetectionAttribute : Attribute
{
}

引用前一个的第二个程序集:

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

[assembly: BreakVSTestFrameworkDetection]

[InheritedExport]
public interface IGenericService<T>
{
    void Print(T thing);
}

public class SomeGenericService<T> : IGenericService<T>
{
    public void Print(T thing) => Console.WriteLine($"{typeof(T)}:{thing}");
}

[TestClass]
public class UnitTest1
{
    [TestMethod]
    public void Run()
    {
        using (var catalogue = new ApplicationCatalog())
        using (var container = new CompositionContainer(catalogue))
        {
            container.GetExportedValue<IGenericService<string>>().Print("asdf"); // System.String:asdf
            container.GetExportedValue<IGenericService<int>>().Print(123); // System.Int32:123
        }
    }

    static void Main(string[] args) => new UnitTest1().Run();
}

通过这两个程序集和定义的测试,我在不同的场景中得到以下结果:

  1. 使用 Visual Studio 2015 Community Editions 的 GUI 运行测试工作得非常好。

    测试资源管理器 / MefGenericsUnitTests (1) / 1 测试通过

  2. 直接将代码作为可执行文件运行可以正常工作。

    C:\Users\ohnob\OneDrive\Documents\Visual Studio 2015\Projects\MefGenericsUnitTests>bin\Debug\MefGenericsUnitTests.exe
    System.String:asdf
    System.Int32:123
    
  3. 使用类似 mstest 的东西运行测试(除了我不知道为什么会或不会使用它而不是 vstest)神奇地起作用(至少在这种情况下)(输出修剪)。

    C:\Users\ohnob\OneDrive\Documents\Visual Studio 2015\Projects\MefGenericsUnitTests>"\Program Files (x86)\Microsoft Visual Studio 14.0\Common7\IDE\MSTest.exe" /testcontainer:bin\Debug\MefGenericsUnitTests.exe
    Microsoft (R) Test Execution Command Line Tool Version 14.0.23107.0
    Copyright (c) Microsoft Corporation. All rights reserved.
    
    Loading bin\Debug\MefGenericsUnitTests.exe...
    Starting execution...
    
    Results               Top Level Tests
    -------               ---------------
    Passed                UnitTest1.Run
    1/1 test(s) Passed
    
  4. 直接运行vstest.console.exe失败(输出修剪):

    C:\Users\ohnob\OneDrive\Documents\Visual Studio 2015\Projects\MefGenericsUnitTests>"\Program Files (x86)\Microsoft Visual Studio 14.0\Common7\IDE\CommonExtensions\Microsoft\TestWindow\vstest.console.exe" bin\Debug\MefGenericsUnitTests.exe
    Microsoft (R) Test Execution Command Line Tool Version 14.0.24720.0
    Copyright (c) Microsoft Corporation.  All rights reserved.
    
    Starting test execution, please wait...
    Failed   Run
    Error Message:
       Test method UnitTest1.Run threw exception:
    System.ComponentModel.Composition.ImportCardinalityMismatchException: No exports were found that match the constraint:
            ContractName    IGenericService(System.String)
            RequiredTypeIdentity    IGenericService(System.String)
    
  5. vstest.console.exe /Framework:Framework45成功:

    C:\Users\ohnob\OneDrive\Documents\Visual Studio 2015\Projects\MefGenericsUnitTests>"\Program Files (x86)\Microsoft Visual Studio 14.0\Common7\IDE\CommonExtensions\Microsoft\TestWindow\vstest.console.exe" /Framework:Framework45 bin\Debug\MefGenericsUnitTests.exe
    Microsoft (R) Test Execution Command Line Tool Version 14.0.24720.0
    Copyright (c) Microsoft Corporation.  All rights reserved.
    
    Starting test execution, please wait...
    Passed   Run
    
    Total tests: 1. Passed: 1. Failed: 0. Skipped: 0.
    Test Run Successful.
    Test execution time: 3.7855 Seconds
    
于 2016-03-19T21:58:38.163 回答
0

我认为你不能使用开放泛型,应该提供一个具体的运行类,如:

var catalog = new TypeCatalog(typeof(Impersonator<type1>), typeof(Impersonator<type2>), typeof(Cache<type3>), typeof(Cache<type4>));
var parts = catalog.Parts;

我尝试使用 IImpersonator 和 ICache 之类的接口,但也不起作用:

interface IImpersonator { }
class Impersonator<T> : IImpersonator
...
var catalog = new TypeCatalog(typeof(IImpersonator), typeof(ICache));
var parts = catalog.Parts;
于 2013-09-20T08:34:35.453 回答