如何构建/部署需要同一程序集的多个版本的项目?

时间:2010-09-05 19:41:26

标签: .net visual-studio assemblies

我正在使用一个使用conflict.dll版本6.2的项目,但该项目还使用了使用conflict.dll版本5.8的helper.dll。

我可以将6.2和5.8安装到GAC中,但是我想让这个项目xcopy可以部署。我相信.net将在应用程序bin目录中搜索程序集,如下所示: \ bin \ conflict.dll(6.2) \ bin \ 5.8 \ conflict.dll(5.8)

但是在这一点上,如何在项目中添加对两个版本的conflict.dll的引用,然后如何确保旧的conflict.dll部署到\ bin \ 5.8?我是否可以创建构建操作,还是有其他方法?

由于

3 个答案:

答案 0 :(得分:14)

经过数小时的搜索和诅咒,我找到了一个有效且易于实施的解决方案。

问题与所有其他答案一样,指出必须满足以下所有要求:

  1. 两个版本的DLL必须具有相同的名称,否则运行时会抱怨该名称与清单不匹配。
  2. 运行时必须能够在搜索路径中找到两个程序集。
  3. 由于更改中断,无法进行版本重定向。
  4. 在此示例中永远不会调用AppDomain.ResolveAssembly,因为程序集已经加载过一次。
  5. 解决方案的步骤如下:

    1. 在解决方案目录中创建一个目录,例如lib\ 这个层次结构:

      的lib \冲突\ V1 \ Conflict.dll
      LIB \冲突\ V2 \ Conflict.dll

    2. 将以下内容添加到app/web.config

    3. <runtime>
        <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
          <dependentAssembly>
            <assemblyIdentity name="Conflict" publicKeyToken="111111111111" />
            <codeBase version="1.0.0.0" href="bin\Conflict\v1\Conflict.dll" />
            <codeBase version="2.0.0.0" href="bin\Conflict\v2\Conflict.dll" />
          </dependentAssembly>
        </assemblyBinding>
      </runtime>
      
      1. 使用xcopy

        添加构建后事件

        xcopy $(SolutionDir)\ lib $(TargetDir)/ Y / S

      2. 构建一次以便复制文件。单击“项目 - &gt;显示所有文件”。右键单击bin\Conflict并执行Include in Project(免于在代码中执行此操作)。如果打包Web应用程序,则必须使用此文件。

      3. 完成!

答案 1 :(得分:3)

  

我相信.net会搜索   应用程序箱中的程序集   像这样的目录:\ bin \ conflict.dll   (6.2)\ bin \ 5.8 \ conflict.dll(5.8)

不,这是错的。我建议你阅读这篇文章,以了解更多关于heuristics does the CLR use for probing的内容。

这就是说,你不能在同一个应用程序域中加载同一程序集的两个不同版本你可以将同一程序集的不同版本加载到同一个应用程序域中,但它是{{3并且应该避免。在您的情况下,这意味着您必须选择要使用的冲突程序集的版本。你有几个选择:

  1. 重新编译helper.dll以使用最新版本的conflict.dll(如果我有helper.dll的源代码,我会定义这个版本。)
  2. 选择所需的conflict.dll版本,并在配置文件中应用considered bad practice。例如,如果您想使用最新版本:

    <runtime>
        <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
            <dependentAssembly>
                <assemblyIdentity name="conflict"
                                  publicKeyToken="xxxxxxxxx"
                                  culture="neutral" />
                <bindingRedirect oldVersion="5.8.0.0" 
                                 newVersion="6.2.0.0" />
            </dependentAssembly>
        </assemblyBinding>
    </runtime>
    
  3. 这将有效地指示CLR在尝试解析conflict.dll的引用时加载helper.dll的6.2版。请注意,如果两个版本使用不同的密钥进行强签名,则此技术将无法使用。显然,如果你有任何差异(缺少方法,不同的方法签名),已经针对版本5.8编译helper.dll,那么在尝试调用冲突方法时会遇到运行时异常,所以只有在你完全确定你是什么时才这样做正在做。

    结论:无论您决定采用哪条路径,您都必须在{strong> bin 文件夹中xcopy conflict.dll的单个版本。

答案 2 :(得分:1)

当然,为了支持达林的答案,你需要消除这种多版本问题。他使用绑定重定向的解决方案很好 - 在那里+1。我可以提供一个解决方案,允许你在绝对必要时保留两者,但你必须写一些代码。

这里唯一真正的问题是两个部署的文件名必须相同才能被加载器默认选中。你可以非常可怕地作弊,并简单地将5.8 dll部署为Conflict.exe,这样它就可以并排Conflict.dll(和更新)并且你会发现它有效。

此外,根据达林回答的链接,你来this topic MSDN topic on probing。基于此内容,您可以简单地将5.8 dll部署到bin \ Content \ Content.dll中,当运行时搜索它时,它将自动查看此子文件夹。

然而 - 这不是一个好的解决方案:)

编辑 - 新解决方案

如果两个版本的Conflict.dll都已签名,您是否真的尝试部署其中一个名称略有不同的版本?我刚刚设置了一个winforms应用程序,其中包含对同一(签名)程序集的不同版本的两个程序集引用。这会导致构建出现一些问题,因为最后引用的版本将部署到bin文件夹,而另一个不会(因此您必须手动复制两者;相应地重命名其中一个)。然后我尝试运行应用程序,它显示一个包含两个常量字符串的消息框;每个版本的程序集中的一个。它的工作非常好。

Download a demo of this here - 构建它(否则你必须重命名文件);只需打开表单应用程序的bin \ debug文件夹并运行exe。

ClassLibrary1.dll和ClassLibary1vanything.dll是程序集的v1.0.0.0和v2.0.0.0,否则具有相同的名称和公钥。尽管classlibrary1vanything.dll具有错误的文件名,但它仍然有效(可能是因为它已签名)。

在app.config中我确实输入了一个代码库提示,并认为这就是它工作的原因(最初我将其部署为不同的文件名),但后来我对它进行了评论,它仍然有用。当程序集必须部署到子文件夹或完全不同的位置时,代码库可能是最有用的。

原文

我试图让in this support article from MS中提到的第二个选项起作用,但似乎并不想这样做。

毫无疑问,有一些聪明的方法可以开箱即用,但由于我还不够聪明(尚未找到它),我会修饰并使用上述支持中显示的第三个选项主题并挂钩到应用域的AssemblyResolve事件。

如果您要为要绑定到不同文件名的程序集的全名添加自己的配置(可能只是在appSettings中),那么在AssemblyResolve事件处理程序中,您可以查询要加载的程序集的名称看看它是否在您的配置中。如果是,抓住该位置并使用Assembly.LoadFrom加载它。

因此,一旦你有这样的东西,你只需在其中添加一个冲突v5.8程序集名称的条目以及应用程序应该使用的文件名。

我不知道您正在部署什么类型的应用程序但是在win表单中,控制台应用程序和服务AppDomain.CurrentDomain.BaseDirectory将等于bin文件夹,您可以使用您想要的文件名加入载入。网站有点棘手。

应该好好享受。