简体   繁体   English

手动加载相关装配

[英]Loading Dependent Assemblies Manually

I have a project that loads multiple versions of the same assembly using either Assembly.Load or Assembly.LoadFile. 我有一个项目,使用Assembly.Load或Assembly.LoadFile加载同一程序集的多个版本。 I then use Assembly.CreateInstance to create a type from that specific assembly. 然后,我使用Assembly.CreateInstance从该特定程序集创建一个类型。

This works great until the type I'm creating references another dependent assembly. 这很有效,直到我创建的类型引用另一个依赖程序集。 I need a way to intercept this specific assembly's request to load another assembly and provide it with the correct version (or, even better, probing path) to look for its dependency. 我需要一种方法来拦截这个特定程序集的请求,以加载另一个程序集并为其提供正确的版本(或更好的探测路径)以查找其依赖项。

This is required because v1 and v2 of the assemblies I'm creating with Assembly.CreateInstance will often need different versions of their dependent assemblies as well, but both v1 and v2 will, by default, probe the same directories. 这是必需的,因为我使用Assembly.CreateInstance创建的程序集的v1和v2通常也需要不同版本的依赖程序集,但默认情况下,v1和v2都将探测相同的目录。

I've seen examples of how to do generally for an AppDomain, but I need to do this in a way that handles all resolution from a particular root assembly. 我已经看到了如何一般地为AppDomain做的示例 ,但我需要以处理来自特定根程序集的所有分辨率的方式执行此操作。 Assuming I do something like: 假设我做了类似的事情:

AppDomain.CurrentDomain.AssemblyResolve += delegate(object sender, ResolveEventArgs args)
{
    //Use args.RequestingAssembly to determine if this is v1 or v2 based on path or whatever
    //Load correct dependent assembly for args.RequestinAssembly
    Console.WriteLine(args.Name);
    return null;
};

This may work for dependencies immediately referenced by my target assembly, but what about the assemblies that those dependencies reference? 这可能适用于我的目标程序集立即引用的依赖项,但这些依赖项引用的程序集又如何呢? If v1 references Depv1 which itself references DepDepv1, I'll need to be able to know this so I can ensure it can find them properly. 如果v1引用了Depv1,它本身引用了DepDepv1,我需要能够知道这一点,这样我才能确保它能正确找到它们。

In that case, I supposed I would need to track this somehow. 在那种情况下,我想我需要以某种方式跟踪这个。 Perhaps by adding custom assembly evidence - although I haven't been able to get that to work, and there doesn't appear to be any "assembly meta data" property that I can add to at runtime. 也许通过添加自定义程序集证据 - 虽然我无法使其工作,并且似乎没有任何“程序集元数据”属性,我可以在运行时添加。

It would be far, far easier if I could simply instruct a particular assembly to load all its dependencies from a particular directory. 如果我可以简单地指示特定程序集从特定目录加载其所有依赖项,那将会容易得多。

Update 更新

I managed to use the AssemblyResolve event to load the dependent assemblies based on the path of the RequestingAssembly, but it seems to be a flawed approach. 我设法使用AssemblyResolve事件根据RequestingAssembly的路径加载依赖程序集,但它似乎是一个有缺陷的方法。 It seems as though the which dependent assembly version while be used is entirely dependent on which version happens to be loaded first. 似乎使用哪个依赖程序集版本完全取决于首先加载哪个版本。

For instance: 例如:

  1. Load v1 加载v1
  2. Load v2 加载v2
  3. Reference v1 causes load of Depv1 参考v1导致Depv1的加载
  4. Reference v2 causes load of Depv2 参考v2导致Depv2的加载
  5. Code in v1 uses type from Depv1 (Works) v1中的代码使用Depv1中的类型(Works)
  6. Code in v2 uses type from Depv2 <-- fails because it gets type from Depv1! v2中的代码使用Depv2中的类型< - 失败,因为它从Depv1获取类型!

I'm only inferring steps 5 and 6 at this point, but I do see Depv1 AND Depv2 being loaded. 我现在只推断步骤5和6,但我确实看到Depv1和Depv2正在加载。

As it turns out, the key to making this work is to ensure you use Assembly.LoadFile. 事实证明,完成这项工作的关键是确保您使用Assembly.LoadFile。 LoadFile is the only method that will load an assembly even if it matches an assembly that .NET thinks is already loaded. LoadFile是加载程序集的唯一方法,即使它与.NET认为已加载的程序集匹配。 I discovered this from an article on codeproject . 我从一篇关于codeproject文章中发现了这一点

Since I needed to load two different assemblies that both had identical full names (ie "App.Test.Domain, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null") but had different contents, LoadFile was the only way to accomplish this. 由于我需要加载两个具有相同全名的不同程序集(即“App.Test.Domain,Version = 1.0.0.0,Culture = neutral,PublicKeyToken = null”)但内容不同,因此LoadFile是唯一的方法这个。 My initial attempts used the Load overload that accepted the type AssemblyName, but it would ignore the path defined in the AssemblyName instance and instead return the already loaded type. 我的初始尝试使用了接受类型AssemblyName的Load重载,但是它将忽略AssemblyName实例中定义的路径,而是返回已经加载的类型。

To force an entire dependency graph to load from a specific location regardless of what other types are already loaded is to register for the AssemblyResolve event: 要强制从特定位置加载整个依赖关系图, 而不管已加载的其他类型是注册AssemblyResolve事件:

AppDomain.CurrentDomain.AssemblyResolve += ResolveDependentAssembly;

And ensure that we use LoadFile to load the dependency: 并确保我们使用LoadFile加载依赖项:

private Assembly ResolveDependentAssembly(object sender, ResolveEventArgs args)
{
    var requestingAssemblyLocation = args.RequestingAssembly.Location;

    if (thePathMatchesSomeRuleSoIKnowThisIsWhatIWantToIntercept)
    {
        var assemblyName = new AssemblyName(args.Name);
        string targetPath = Path.Combine(Path.GetDirectoryName(requestingAssemblyLocation), string.Format("{0}.dll", assemblyName.Name));
        assemblyName.CodeBase = targetPath; //This alone won't force the assembly to load from here!

        //We have to use LoadFile here, otherwise we won't load a differing
        //version, regardless of the codebase because only LoadFile
        //will actually load a *new* assembly if it's at a different path
        //See: http://msdn.microsoft.com/en-us/library/b61s44e8(v=vs.110).aspx
        return Assembly.LoadFile(assemblyName.CodeBase);
    }

    return null;
}

Yes, this code assumes that if your root assembly has dependencies, that they're all located at the same path. 是的,此代码假定如果根组件具有依赖关系,则它们都位于相同的路径中。 That's a limitation, no doubt, but you could fairly easily add additional hints for non-local dependencies. 毫无疑问,这是一个限制,但您可以相当容易地为非本地依赖项添加其他提示。 This also would only be an issue if the already loaded version of those additional dependencies wouldn't work. 如果这些附加依赖项的已加载版本不起作用,这也只是一个问题。

Lastly, none of this would be necessary if the assembly versions were properly incremented. 最后,如果组件版本正确递增,则不需要这些。 The Load call would not treat an already loaded Depv1 as the same as a request Depv2. Load调用不会将已加载的Depv1视为与请求Depv2相同。 In my case, that wasn't something I was willing to deal with as part of my continuous integration and deployment process. 就我而言,作为我持续集成和部署过程的一部分,这不是我愿意处理的事情。

Try Assembly.LoadFrom(path); 试试Assembly.LoadFrom(path); which will resolve dependencies automatically. 这将自动解决依赖关系。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM