在我正在进行的项目中,我们已经开展了大量工作来设计表单样式并使其更符合业务规则。这涉及诸如填充表格,更改标签,更改列标题等内容 - 基本上是一堆东西。
我被要求研究制作工具的可行性,以便比较我们的原始形式与新形式,以查看已经改变的内容。我已经走了使用反射的路线 - 获取原始DLL并依次加载表单并比较控件(我想不出更简单的方法来做到这一点,因为我们的很多表单都有其他项目在运行时“注入”,因此设计器文件的比较不起作用。
在我的项目中,我创建了两个名为“OriginalAssemblies”和“CurrentAssemblies”的目录,并且每个目录中都有一个依赖项目录,其中包含表单所依赖的所有程序集。这些文件主要在Original和Current之间有所不同。
我的第一个方法是根据一个目录读入程序集,然后将它们存储在一个列表中 - 有一个用于原始,一个用于当前:
private static void LoadInAssemblies(string sourceDir, List<Assembly> assemblyList)
{
DirectoryInfo dir = new DirectoryInfo(sourceDir);
foreach (FileInfo currentFile in dir.GetFiles().Where(f => f.Name.Contains("dll")))
{
assemblyList.Add(Assembly.LoadFile(currentFile.FullName));
}
}
这似乎工作正常,因为它加载包含表单的DLL。接下来,我有一个方法,以不同的形式加载并处理它们。这会运行,但每次都会从同一目录中加载依赖项
foreach (Assembly current in originalAssemblies)
{
var items = current.GetTypes().Where(t => t.IsSubclassOf(typeof(Form)) && !t.Name.Contains("Base")).ToList();
foreach (var newForm in items)
{
Object originalForm = Activator.CreateInstance(newForm);
// get the associated assembly from the new form
Object currentForm = Activator.CreateInstance (currentAssemblies.SelectMany(a => a.GetTypes()).Where (t => t.Name == newForm.Name).First());
((Form)originalForm).ShowDialog();
((Form)currentForm).ShowDialog();
}
}
经过一些研究后,我在app.config中找到了探测的参考,所以设置如下:
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<probing privatePath="OriginalAssemblies/Dependencies"/>
</assemblyBinding>
</runtime>
现在我意识到我可以分开目录并拥有Current和Original目录,但在这种情况下它将无济于事,因为依赖项文件夹中的DLL将是相同的 - 只是不同的程序集版本。
我的问题是,根据上面的代码,在创建对象currentForm以使用正确的目录时,我可以告诉GetType()命令,在本例中是CurrentAssemblies吗?
答案 0 :(得分:2)
您的问题是,当加载程序集时,它将保留在内存中,随后对需要该程序集的程序集的调用将自动使用它。这就是AppDomain.AssemblyResolve
无法帮助您并造成更多麻烦的原因。
我的方法就是这样
创建一个工具,用于创建表单并将其转储到文本文件中。然后这个工具两次,第一次在原始,第二次在修改文件夹中。使用您喜欢的任何文本比较工具比较文本文件,或者如果您愿意,可以比较自己。
答案 1 :(得分:2)
我已经加载了同一种dll的多个版本。这就是我想出的:
您需要在两个 AppDomain
上使用AssemblyResolve事件来自动处理项目中引用的dll文件的重定向(这里是我的&#34;当前&# 34; dll在&#34; bin&#34;&#34;原始&#34;在&#34; bin / original&#34;):
private static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
// Path for to "Current" dlls.
string newPath = Path.Combine(Path.GetDirectoryName(Assembly.GetCallingAssembly().Location),
"bin", new AssemblyName(args.Name).Name + ".dll");
if (File.Exists(newPath))
return Assembly.LoadFrom(newPath);
return args.RequestingAssembly;
}
private static Assembly CurrentDomain_AssemblyResolve_Original(object sender, ResolveEventArgs args)
{
// Path for the "Original" dlls.
string newPath = Path.Combine(Path.GetDirectoryName(Assembly.GetCallingAssembly().Location),
"bin", "original", new AssemblyName(args.Name).Name + ".dll");
if (File.Exists(newPath))
return Assembly.LoadFrom(newPath);
return args.RequestingAssembly;
}
重要强> 确保尽快将代码放在下面(在第一个被调用的构造函数中),或者不要调用事件订阅!!:
private static readonly AppDomain OriginalAppDomain;
// Here, "Program" is my first class constructor called on startup.
static Program()
{
AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve;
AppDomain.CurrentDomain.ReflectionOnlyAssemblyResolve += CurrentDomain_AssemblyResolve;
// Second app domain for old DLLs.
OriginalAppDomain = AppDomain.CreateDomain("Original DLLs", new Evidence());
OriginalAppDomain.AssemblyResolve += CurrentDomain_AssemblyResolve_Original;
OriginalAppDomain.ReflectionOnlyAssemblyResolve += CurrentDomain_AssemblyResolve_Original;
}
现在你可以像这样一次测试两个dll:
// Runs on current AppDomain.
new SpecialForm().ShowDialog();
// Runs on OriginalAppDomain (created above).
OriginalAppDomain.DoCallBack(() => new SpecialForm().ShowDialog());
可能的改进
也可以通过检查版本而不是目录来更改AssemblyResolve
方法。因此,您可以更好地控制将加载Assembly
。