2014-09-01 32 views
2

在我目前正在开发的项目上,我们已经开展了大量工作来设计窗体风格并使其与业务规则更加一致。这涉及到诸如填充表单,更改标签,更改列标题等内容 - 基本上加载了一些东西。C#反思 - 在运行时确定依赖项的位置

我被要求研究生产一种工具的可行性,以比较我们的原始形式和新的形式,看看发生了什么变化。我已经沿着使用反射的路线 - 获取原始DLL并依次加载表单并比较控件(我想不出一个更简单的方法来做到这一点,因为我们的很多表单都有其他项目“注入”在运行时,所以设计器文件的比较将不起作用

在我的项目中,我创建了两个名为“OriginalAssemblies”和“CurrentAssemblies”的目录,并且在每个目录中都有一个依赖目录,其中包含所有依赖于窗体的程序集,这些文件的主要区别在于原始和当前

我的第一个方法基于目录读取程序集,然后存储在列表中 - 有一个用于原创,另一个用于当前:

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将是相同的 - 只是不同的程序集版本。

我的问题是,根据上面的代码,有什么办法可以告诉GetType()命令创建对象currentForm时使用正确的目录,在这种情况下会是CurrentAssemblies?

+1

你可以尝试AppDomain.AssemblyResolve事件 – bdn02 2014-09-01 14:38:32

+0

安德鲁,你有帮助吗?请将答案标记为已接受,或提供有关您卡住的详细信息。 – Measuring 2014-09-02 16:47:37

回答

2

我已经去加载同一种DLL的多个版本。这是我想出了:

你需要使用的 AppDomain的AssemblyResolve事件自动处理引用dll文件重定向在您的项目(在这里我的“当前”的DLL中的“bin “和‘原创’是在‘斌/原’):

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; 
} 

重要 确保把下面尽快的代码(被称为第一个构造函数)或事件订阅赢得't be called !!:

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将被加载。

2

你的问题是,当一个程序集加载时,它会留在内存中,随后调用程序集时需要程序集自动使用它。这就是为什么AppDomain.AssemblyResolve不会帮助你,并造成更多麻烦。

我的做法会是这样

创建一个用于创建表单和转储成文本文件的工具。然后这个工具在两次,第一次在原始,第二次在修改后的文件夹中。使用你喜欢的任何文本比较工具比较文本文件,或者如果你喜欢,可以比较自己。