使用两个不同版本的相同dll?

32

我已经得到了两个预编译的dll文件:

Common.Data, Version=1.0.0.0, Culture=neutral, PublicKeyToken=f3b12eb6de839f43, processorArchitecture=MSIL

Common.Data, Version=2.0.0.0, Culture=neutral, PublicKeyToken=f3b12eb6de839f43, processorArchitecture=MSIL

API差异示例:

API

我正在尝试将两者加载到一个项目中,以实现类似以下的操作:

extern alias v10;
extern alias v20;

private static void UpgradeUser()
{
    // Load old user
    var userOld = new v10::Common.Data.UserData();
    userOld.loadData("user.dat");

    // Create new user
    var userNew = new v20::Common.Data.UserData();

    // Copy properties  
    userNew.FirstName = userOld._firstName;
    userNew.LastName = userOld._lastName;
    userNew.Age = userOld._age;

    // Invoke method from v10 and v20 API
    userOld.version();
    userNew.DisplayVersion();

    if (userNew.GetUserInfo() != userOld.getInfo())
    {
        throw new Exception("Discrepencies in upgrade ");
    }

    Console.WriteLine("Upgrade done!");
}

我已经设置了我的项目引用和app.config如下。我还手动将dll复制到我的输出文件夹中,与appconfig hrefs匹配。

<!-- [Edited: see history for previous version] -->
<Reference Include="Common.Data, Version=1.0.0.0, Culture=neutral, PublicKeyToken=f3b12eb6de839f43, processorArchitecture=MSIL">
  <HintPath>libs\Common.Data.1_0_0_0.dll</HintPath>
  <Aliases>v10</Aliases>
</Reference>

<Reference Include="Common.Data, Version=2.0.0.0, Culture=neutral, PublicKeyToken=f3b12eb6de839f43, processorArchitecture=MSIL">
  <HintPath>libs\Common.Data.2_0_0_0.dll</HintPath>
  <Aliases>v20</Aliases>
</Reference>

<runtime>
  <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
    <dependentAssembly>
      <assemblyIdentity name="Common.Data" publicKeyToken="f3b12eb6de839f43" culture="neutral" />
      <codeBase version="1.0.0.0" href="libs\Common.Data.1_0_0_0.dll" />
      <codeBase version="2.0.0.0" href="libs\Common.Data.2_0_0_0.dll" />
    </dependentAssembly>
  </assemblyBinding>
</runtime>

我已经成功构建了这个程序。

但是当我尝试运行它时,UserData.loadData 出现了一个 MissingMethodException 错误。

Error

我已经查看了多个stackoverflow帖子、msdn和codeproject文章,但似乎无法使其正常工作。

链接1, 链接2, 链接3, 链接4

我认为我可能错过了一些重要的步骤,但却找不出来,真的需要一些帮助。

[编辑1]

我尝试了单独使用dll文件,它们可以工作。(删除了杂乱的内容。有关屏幕截图,请参阅版本历史记录)

[编辑2]

我尝试了Mukesh Kumar的建议,并将我的app.config更改为:

<runtime>
  <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
    <dependentAssembly>
      <assemblyIdentity name="Common.Data" 
                        publicKeyToken="f3b12eb6de839f43" 
                        culture="neutral" />
      <bindingRedirect oldVersion="1.0.0.0"
                       newVersion="2.0.0.0"/>
    </dependentAssembly>
  </assemblyBinding>
</runtime>

但它仍然无法工作。现在我遇到了FileNotFoundException。
[编辑3]
好的,我几乎可以确定不正确,应该改为。
我尝试从CLI编译:
csc Program.cs /reference:v10=libs/Common.Data.1_0_0_0.dll /reference:v20=libs/Common.Data.2_0_0_0.dll
它可以正常工作。我能够同时使用两个API:

enter image description here

但是当我尝试从Visual Studio构建它时,它抱怨/reference,即使我已经指定了引用别名:
The extern alias 'v10' was not specified in a /reference option
我尝试修改<Reference />,包括/排除<SpecificVersion>True</SpecificVersion>等,但没有效果。
我发现了这个post,解决方法是删除并重新添加路径。使用该解决方案,Visual Studio可以成功构建,但出现了System.MissingMethodException
感觉我快要做到了,但还差一点。如何让Visual Studio正确构建?
[编辑4]
我尝试了miguel的方法,但仍然不起作用。
在这个尝试中,我将dll文件重命名为它们的原始名称,并将它们存储在不同的文件夹中。

enter image description here

我随后更新了 app.config,进行绑定重定向以及 codebase。
运行时出现 "MissingMethodException" 错误。
我不确定 "4- Find the and put in False" 是什么意思,所以我尝试所有 "", "" 的组合,以及将 "" 设置为 FQNs,但是这些组合都没有起作用。
通过查看我的第三个编辑,当通过 CLI 完成时,我已经成功编译和运行了示例(同时通过修改的 dlls + app.config 的 codebase 链接)。现在我的问题是,如何配置我的 csproj,使其能够构建相同的东西。

请问您能提供错误堆栈信息吗? - lokusking
方法找不到的原因可能是它试图使用错误的程序集。为什么loadDataCommon.Data.UserDa..会被标记成红色?这是Resharper的错误吗?它说了什么? - caesay
@caesay,是的,这是一个resharper错误“无法解析符号'loadData'”,尽管VS构建成功。dll已被重命名,这就是为什么我在appconfig中添加了runtime元素的原因。如果没有它,我会得到一个FileNotFoundException - Could not load file or assembly 'Common.Data... - jayars
奇怪。也许你可以尝试在可执行文件的同一文件夹中使用正常名称导入从Main调用的DLL,或编译自己的一对DLL来检查库中是否存在问题。 - IS4
@jayars - 我看了你图片中的绑定重定向,你告诉运行时无论如何都要将自己重定向到v 2.0.0.0。你把1.0.0.0的dll从图片中剔除了。第一个绑定重定向应该改为0.0.0.0 - 1.0.0.0 => 1.0.0.0,而不是0.0.0.0 - 2.0.0.0 => 2.0.0.0。我实际上认为你甚至不需要这两个bindingRedirect标签。 - N73k
显示剩余2条评论
3个回答

21

您需要使用dependentAssembly和bindingRedirect,但也需要将dll放在不同的文件夹中或用不同的名称保存。完成此操作后,您需要将以下内容放入您的app.config中:

你需要使用 bindingRedirect 和 dependentAssembly,在配置文件 app.config 中指定已经重定向的程序集名字和版本号,同时确保这些程序集被正确地放在相应的文件夹中,或者使用不同的名称保存。

<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
  <dependentAssembly>
        <assemblyIdentity name="myAssembly"
                          publicKeyToken="here token dll"
                          culture="neutral" />
       <bindingRedirect oldVersion="0.0.0.0-1.0.0.0" newVersion="1.0.0.0" />
       <bindingRedirect oldVersion="1.0.0.1-2.0.0.0" newVersion="2.0.0.0" />
       <codeBase version="1.0.0.0" href="folder\namedll.dll" />
       <codeBase version="2.0.0.0" href="folder\namedll.dll" />
      </dependentAssembly>
   </assemblyBinding>

使用这段代码应该可以编译和运行,但有时VS在编译时会删除或覆盖app.config中的代码。您需要在编译文件夹的配置文件中进行检查。如果成功,您可以编辑.csproj文件。操作如下:

1- 卸载受影响的项目
2- 右键单击项目
3- 单击“编辑项目”
4- 找到<AutoGenerateBindingRedirects> 属性并将其设置为False
5- 保存更改并重新加载项目

这对我有用。在我的项目中,我正在使用Automapper的两个版本。

最后,另一种解决方案是使用AppDomain.CurrentDomain.AssemblyResolve构建事件并加载特定的dll。

为此,您需要捕获事件:

AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolve);

public static System.Reflection.Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
    //debug and check the name
    if (args.Name == "MyDllName")
        return Assembly.LoadFrom("c:\\pathdll\midllv1.dll")
    else if(args.Name ="MyDllName2")
        return Assembly.LoadFrom("c:\\pathdll\midllv2.dll");
    else
        return Assembly.LoadFrom("");
}

我尝试了这个(参见Edit4),但仍然遇到了相同的“MissingMethodException”。 我很确定只有 <codeBase /> 就足够了(Edit3)。 我只是不知道如何配置我的csproj文件,使其模拟csc CLI编译。 - jayars
@jsjslim,我在你的app.config代码中看到BindingRedirect的版本不正确,你需要定义一个bindingRedirect,如´<bindingRedirect oldVersion="0.0.0.0-1.0.0.0" newVersion="1.0.0.0"/>´和´<bindingRedirect oldVersion="1.0.0.1-2.0.0.0" newVersion="2.0.0.0"/>´。当我说“4-查找并将其设置为False”时,我想说的是将属性<AutoGenerateBindingRedirects>设置为False。 - Miguel Remirez
1
感谢您的编辑。对我来说,是<AutoGenerateBindingRedirect>false</<AutoGenerateBindingRedirect>解决了问题。尽管VS和Resharper都有警告,但构建成功并按预期运行。 - jayars
1
对于网站,href应该以“bin\”开头,例如<codeBase version="1.0.0.0" href="bin\folder\namedll.dll" />。 - Cyclion
@miguel - 但是如何在Visual Studio中添加两个同名但不同版本的dll引用?当我尝试通过“添加引用”>“浏览”来添加时,会出现“A reference to 'aaa.dll" could not be added. A reference to the component 'aaa' already exists in the project."的错误提示。 - N73k
是否可以将更改放入web.config文件中? - user3748973

1

由于没有符合我的需求的完整答案,我决定通过使用AssemblyResolve方法来捐赠我的解决方案,以使用两个dll并从文件中加载相关dll,使用反射来解析类型。为了演示,我创建了两个名为MathFuncs的dll,试图调用它们的Add.add函数将导致两种不同的实现。要查看不同的结果,请在值1和2之间切换版本整数:

public static int version = 1;
    public static void Main(string[] args)
    {
        AppDomain.CurrentDomain.AssemblyResolve += Program.CurrentDomain_AssemblyResolve;
        version = 1;
        Type a = Type.GetType("MathFuncs.Add, MathFuncs, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null");
        MethodInfo methodInfo = a?.GetMethod("add");
        object result = null;
        if (methodInfo != null)
        {
            object[] parametersArray = new object[] {1, 2};
            result = methodInfo.Invoke(Activator.CreateInstance(a, null), parametersArray);
        }

        if (result != null)
        {
            Console.WriteLine((int)result);
        }
        else
        {
            Console.WriteLine("failed");
        }

        Console.Read();
    }


    public static System.Reflection.Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
    {
        FileInfo fi = null;
        if (version == 1)
        {
            fi = new FileInfo("C:\\Users\\ohbitton\\Desktop\\MathFuncs\\MathFuncs.dll");
        }
        else
        {
            fi = new FileInfo("C:\\Users\\ohbitton\\Desktop\\MathFuncs2\\MathFuncs.dll");
        }
        return Assembly.LoadFrom(fi.FullName);

    }

要获取 Type.GetType 命名空间的完整名称,您可以使用 PowerShell:

([system.reflection.assembly]::loadfile("C:\Users\ohbitton\Desktop\MathFuncs\MathFuncs.dll")).FullName

0

尝试按照以下方式放置您的配置细节。

 <dependentAssembly>
            <assemblyIdentity name="myAssembly"
                              publicKeyToken="32ab4ba45e0a69a1"
                              culture="neutral" />
            <bindingRedirect oldVersion="1.0.0.0"
                             newVersion="2.0.0.0"/>
  </dependentAssembly>

在这里,您可以提供您的DLL旧版本和新版本。您只需按照此文章此链接即可。

希望这能帮助到您。


谢谢回复。不幸的是,它仍然无法工作。在使用<bindingRedirect>时,我遇到了FileNotFoundException。请注意,在我的帖子中,PublicKeyToken是相同的,API已更改,并且我正在尝试同时使用两个API,而不使用Reflection(以免失去智能感和编译时检查)。我已更新我的问题以包含更多信息。 - jayars

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接