在我正在处理的项目中,我创建了一个AppDomain
并告诉它加载当前正在执行的程序集。然后我调用Activator.CreateInstanceFrom
来创建内部类(类型为MyType
)的编组实例(并且您可以看到它也使用相同的程序集位置):
ObjectHandle handle = Activator.CreateInstanceFrom(
Factory.operatingDomain,
Assembly.GetExecutingAssembly().Location,
typeof(MyType).FullName,
false,
BindingFlags.NonPublic | BindingFlags.Instance,
null,
new object[] { config },
null,
null);
当我对此程序集进行单元测试时,一切正常,但在ASP.NET调试环境中使用它会抛出MissingMethodException
。我花时间创建了自己的类,扩展Binder
以查看会发生什么。唯一的区别似乎是在调试环境中,传入的参数(config
)从位于常规目录中的程序集中获取其类型,而ParameterInfo
对象从位于程序集中的程序集中获取其类型在ASP.NET临时文件夹中。
即使我通过typeof(config).Assembly.Location
,我也会得到相同的结果。所以这是框架内部的东西。
除了保留一个利用内部可访问的自定义绑定器,当参数类型根据FullName
匹配时返回true,我还能做些什么来纠正这个问题吗?
更新
我已经尝试传递从app域加载的程序集中获取的程序集的位置,但仍然没有骰子。我的自定义绑定器也不起作用 - 它需要我实现Binder.ChangeType,我发现我甚至无法从一种类型转换到另一种类型(尽管如上所述,它们之间的唯一区别是位置大会。)
更新2
我尝试了以下代码,以确保应用程序域从正确的位置加载正确的程序集:
if (binPath != String.Empty)
{
Factory.operatingDomain.SetData("assemblyLocation", Assembly.GetExecutingAssembly().Location);
Factory.operatingDomain.DoCallBack(() =>
{
String location = AppDomain.CurrentDomain.GetData("assemblyLocation").ToString();
String filename = System.IO.Path.GetFileName(location);
List<String> paths = AppDomain.CurrentDomain.RelativeSearchPath.Split(';').ToList();
foreach (String path in paths.ToArray())
{
paths.Remove(path);
paths.AddRange(System.IO.Directory.GetFiles(path, filename));
}
Assembly.LoadFrom(paths[0]);
});
}
这是对的!它加载了正确的组件!但是在这里,过了一会儿:
String location = Factory.operatingDomain
.GetAssemblies()
.Single(a => a.FullName == Assembly.GetExecutingAssembly().FullName)
.Location;
这将返回.NET临时文件夹路径。那是不对的。我认为这是框架中的一个错误!
答案 0 :(得分:1)
当您向应用程序域询问其某个程序集位置时,.NET框架似乎无法正确解析程序集位置。为了解决这个问题,我不得不自己探测装配:
// This code I was already using:
String binPath = String.Empty;
if (!String.IsNullOrEmpty(AppDomain.CurrentDomain.RelativeSearchPath))
{
String[] paths = AppDomain.CurrentDomain.RelativeSearchPath.Split(';');
for (var i = 0; i < paths.Length; i++)
{
paths[i].Remove(0, AppDomain.CurrentDomain.BaseDirectory.Length);
}
binPath = String.Join(";", paths);
}
Factory.operatingDomain = AppDomain.CreateDomain("my_remote_domain", null,
new AppDomainSetup
{
ApplicationBase = AppDomain.CurrentDomain.BaseDirectory,
// Sometimes, like in a web app, your bin folder is not the same
// as the base dir.
PrivateBinPath = binPath
});
// The new code that solved my problem begins here:
if (binPath != String.Empty)
{
Factory.operatingDomain.SetData("assemblyLocation", Assembly.GetExecutingAssembly().Location);
Factory.operatingDomain.DoCallBack(() =>
{
String location = AppDomain.CurrentDomain.GetData("assemblyLocation").ToString();
String filename = System.IO.Path.GetFileName(location);
List<String> paths = AppDomain.CurrentDomain.RelativeSearchPath.Split(';').ToList();
foreach (String path in paths.ToArray())
{
paths.Remove(path);
paths.AddRange(System.IO.Directory.GetFiles(path, filename));
}
Assembly.LoadFrom(paths[0]);
AppDomain.CurrentDomain.SetData("assemblyLocation", paths[0]);
});
Factory.realAssemblyLocation = Factory.operatingDomain.GetData("assemblyLocation").ToString();
}
else
{
Factory.operatingDomain.Load(Assembly.GetExecutingAssembly().FullName);
}
然后当我尝试创建远程对象的实例时,我这样做:
String location = Factory.realAssemblyLocation ?? Assembly.GetExecutingAssembly().Location;
ObjectHandle handle = Activator.CreateInstanceFrom(
Factory.operatingDomain,
location,
typeof(MyType).FullName,
false,
BindingFlags.NonPublic | BindingFlags.Instance,
null,
new object[] { config },
null,
null);
事实证明,我真正所有必须做的就是将新AppDomain的ApplicationBase设置为包含当前正在执行的程序集的目录。更简单:)
答案 1 :(得分:1)
你的申请是什么? ASP.NET应用程序?如果是,你应该知道一个名为程序集卷影副本的野兽(见http://msdn.microsoft.com/en-us/library/ms404279.aspx)。这个东西在ASP.NET中默认使用。简而言之,当您使用位于bin文件夹中的程序集以及ASP.NET应用程序程序集时,它将被复制到ASP.NET临时文件夹并从那里加载。
要确保这是导致您遇到的行为的原因,请尝试放置定义&#34; config&#34;的类型的程序集。参数进入某个单独的文件夹并通过捕获AppDomain_AssemblyResolve事件加载程序集或在使用它之前手动加载它。这样您就会忽略程序集shadowcopy行为。