使用 TFS 工件时,在单独的 AppDomain 中找不到 MEF 导出

MEF Export in separate AppDomain not found when using TFS artifact

我有一个简单的 MEF 导入设置,以便允许 运行使用单独的应用程序域及时更新我的​​二进制文件。问题如下:

我们正在办公室里讨论为什么会发生这种情况,特别是因为我们的测试人员使用了神器掉落。

我使用的代码精简版:

接口(Service.Common):

public interface IService
{
    void Config(string machineName, string instanceName, string description);
}

导出 class (Service.Core):

[Export(typeof(IService)), Serializable]
public class Service : IService
{
    public void Config(string machineName, string instanceName, string description)
    {
        Debugger.Break();
    }
}

主持人 class (Service.Host):

[Serializable]
public class ServiceShell
{
    private static readonly string BinPath = Path.Combine(AppDomain.CurrentDomain.SetupInformation.ApplicationBase, "bin");
    private static readonly string CachePath = Path.Combine(AppDomain.CurrentDomain.SetupInformation.ApplicationBase, "cache");
    private static IService _service;
    private static AppDomainSetup _setup;

    private AppDomain _domain;

    public ServiceShell(string machineName, string instanceName, string description)
    {
        Directory.CreateDirectory(BinPath);
        Directory.CreateDirectory(CachePath);

        Console.WriteLine($"Binpath: {BinPath}");
        Console.WriteLine($"Cachepath: {CachePath}");

        Console.WriteLine($"Host domain fully trusted appdomain: {AppDomain.CurrentDomain.IsFullyTrusted}");


        _setup = new AppDomainSetup
        {
            ApplicationName = "Service_Core",
            ApplicationBase = AppDomain.CurrentDomain.SetupInformation.ApplicationBase,
            CachePath = CachePath,
            ShadowCopyFiles = "true",
            ConfigurationFile = Path.Combine(BinPath, Directory.GetFiles(BinPath, "*.dll.config")[0]),
            AppDomainInitializer = ServiceDomainConfig,
            AppDomainInitializerArguments = new[] { machineName, instanceName, description }
        };

        Console.WriteLine(_setup.ConfigurationFile);

        Compose();
    }

    private static void ServiceDomainConfig(string[] arguments)
    {
        var aggregateCatalog = new AggregateCatalog();
        aggregateCatalog.Catalogs.Add(new DirectoryCatalog(BinPath));
        var container = new CompositionContainer(aggregateCatalog);

        Console.WriteLine($"Service domain fully trusted appdomain: {AppDomain.CurrentDomain.IsFullyTrusted}");

        _service = container.GetExportedValue<IService>();
        _service.Config(arguments[0], arguments[1], arguments[2]);
    }

    private void Compose()
    {
        if (_domain != null)
            AppDomain.Unload(_domain);

        try
        {
            _domain = AppDomain.CreateDomain("ServiceDomain", AppDomain.CurrentDomain.Evidence, _setup);
        }
        catch(Exception ex)
        {
            Console.WriteLine($"Unable to create domain: {ex}");
            throw;
        }
    }

相关错误的示例输出:

Binpath: C:\Builds\Service\bin
Cachepath: C:\Builds\Service\cache
Host domain fully trusted appdomain: True
C:\Builds\Service\bin\Service.Core.dll.config
Service domain fully trusted appdomain: True
Unable to create domain: System.ComponentModel.Composition.ImportCardinalityMismatchException: No exports were found that match the constraint:
        ContractName Service.Common.IService
        RequiredTypeIdentity Service.Common.IService
   at System.ComponentModel.Composition.Hosting.ExportProvider.GetExports(ImportDefinition definition, AtomicComposition atomicComposition)
   at System.ComponentModel.Composition.Hosting.ExportProvider.GetExportsCore(Type type, Type metadataViewType, String contractName, ImportCardinality cardinality)
   at System.ComponentModel.Composition.Hosting.ExportProvider.GetExportedValueCore[T](String contractName, ImportCardinality cardinality)
   at Service.Host.ServiceShell.ServiceDomainConfig(String[] arguments)
   at System.AppDomain.RunInitializer(AppDomainSetup setup)
   at System.AppDomain.Setup(Object arg)
   at System.AppDomain.nCreateDomain(String friendlyName, AppDomainSetup setup, Evidence providedSecurityInfo, Evidence creatorsSecurityInfo, IntPtr parentSecurityDescriptor)
   at System.AppDomain.InternalCreateDomain(String friendlyName, Evidence securityInfo, AppDomainSetup info)
   at System.AppDomain.CreateDomain(String friendlyName, Evidence securityInfo, AppDomainSetup info)
   at Service.Host.ServiceShell.Compose()

Unhandled Exception: System.ComponentModel.Composition.ImportCardinalityMismatchException: No exports were found that match the constraint:
        ContractName Service.Common.IService
        RequiredTypeIdentity Service.Common.IService
   at System.ComponentModel.Composition.Hosting.ExportProvider.GetExports(ImportDefinition definition, AtomicComposition atomicComposition)
   at System.ComponentModel.Composition.Hosting.ExportProvider.GetExportsCore(Type type, Type metadataViewType, String contractName, ImportCardinality cardinality)
   at System.ComponentModel.Composition.Hosting.ExportProvider.GetExportedValueCore[T](String contractName, ImportCardinality cardinality)
   at Service.Host.ServiceShell.ServiceDomainConfig(String[] arguments)
   at System.AppDomain.RunInitializer(AppDomainSetup setup)
   at System.AppDomain.Setup(Object arg)
   at System.AppDomain.nCreateDomain(String friendlyName, AppDomainSetup setup, Evidence providedSecurityInfo, Evidence creatorsSecurityInfo, IntPtr parentSecurityDescriptor)
   at System.AppDomain.InternalCreateDomain(String friendlyName, Evidence securityInfo, AppDomainSetup info)
   at System.AppDomain.CreateDomain(String friendlyName, Evidence securityInfo, AppDomainSetup info)
   at Service.Host.ServiceShell.Compose()
   at Service.Host.ServiceShell..ctor(String machineName, String instanceName, String description)
   at Service.Host.Program.Main()

我已经找到导致此问题的原因,但它并不像看起来那么明显。问题实际上是 Windows.

中的安全问题

下载的 TFS drop 是来自互联网的 zip,这导致 Windows 自动将 zip 标记为可能不安全并向 zip 添加备用数据流。当您使用资源管理器解压缩时,该 zip 内的所有文件都会将相同的 ADS 传输给它们。结果是 MEF 不享受这个 ADS,导致它找不到出口。当我删除 ADS 层时,它发现导出很好,类似地,当我尝试解锁 zip 并解压缩时,它发现导出很好。

不同之处在于 web-host 在我们的内部网络中,所以当我将文件复制到我的本地 PC 时,它没有收到这个不安全标志,因此也没有收到 ADS 层。