使用 TFS 工件时,在单独的 AppDomain 中找不到 MEF 导出
MEF Export in separate AppDomain not found when using TFS artifact
我有一个简单的 MEF 导入设置,以便允许 运行使用单独的应用程序域及时更新我的二进制文件。问题如下:
- 当我运行调试配置中Visual Studio中的代码时:有效
- 当我运行 Visual Studio 中的代码发布配置时:有效
- 当我 运行 使用 TFS 工件的代码在发布时下降
配置:找不到导出
- 当我使用 PS 脚本手动压缩和上传(形式 POST)
TFS 构建到 Web 主机,然后将该版本复制到我的本地 PC:
作品
我们正在办公室里讨论为什么会发生这种情况,特别是因为我们的测试人员使用了神器掉落。
我使用的代码精简版:
接口(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 层。
我有一个简单的 MEF 导入设置,以便允许 运行使用单独的应用程序域及时更新我的二进制文件。问题如下:
- 当我运行调试配置中Visual Studio中的代码时:有效
- 当我运行 Visual Studio 中的代码发布配置时:有效
- 当我 运行 使用 TFS 工件的代码在发布时下降 配置:找不到导出
- 当我使用 PS 脚本手动压缩和上传(形式 POST) TFS 构建到 Web 主机,然后将该版本复制到我的本地 PC: 作品
我们正在办公室里讨论为什么会发生这种情况,特别是因为我们的测试人员使用了神器掉落。
我使用的代码精简版:
接口(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 层。