强制ASP.NET应用程序从bin文件夹加载程序集而不是从GAC加载

20

有没有办法强制我的 asp.net 应用程序从本地 bin 目录加载程序集,因为 GAC 中有另一个同名的旧版本程序集?

我不能删除 GAC 版本,因为其他应用程序正在使用它,而将新版本添加到 GAC 时遇到一些困难。


1
你设置了“Specific Version”为true吗?如果是的话,它不应该使用GAC中的旧版本。 - Oliver Bock
8个回答

8
我找到了它
要强制你的应用程序从本地 bin 目录读取,你需要从程序集中删除签名,然后应用程序将从 bin 中加载程序集。
感谢 Wyatt Barnett 和 murad。

4
如果我无法从程序集中删除强名称怎么办? - Amitabh
2
你能提供一下从程序集中移除签名的步骤吗? - dlchambers
1
只需删除强名称文件。 - Khaled Musaied
@Amitabh,你无法从没有源代码的程序集中删除签名。作为一个技巧,你可以将DEVOVERRIDE环境变量设置为包含你的程序集的路径,这将在GAC之前进行探测。 - Abel
@Abel,你有这个DEVOVERRIDE解决方案的链接吗? - RanH
@antisane,我已经将其转化为一个包含相关链接和解释的答案。如果您无法删除签名,则可以使用它。 - Abel

5
改变版本号,对程序集进行强名称,并引用您部署的具有强名称的更高版本解决方案。

1
我尝试过,但它仍会从全局程序集缓存中加载程序集。 - Khaled Musaied
1
忘记添加“更改版本号”步骤了。然后你就万事大吉了。 - Wyatt Barnett

3
作为替代方案,在开发过程中,您可以通过设置 DEVPATH 环境变量 并在 machine.config 中启用 开发模式 来绑定到任何程序集,完全覆盖 GAC。我认为这是实现您想要的最简单的方法,但不应在生产环境中使用。
这解决了您的程序集版本与 GAC 中的程序集版本相同的问题。如果版本不同,您应该使用此处多位用户提到的 bindingRedirect 方法。
首先,将以下内容添加到 machine.config
<configuration>
  <runtime>
    <developmentMode developerInstallation="true"/>
  </runtime>
</configuration>

然后,将DEVPATH环境变量设置为非签名程序集的位置。这将强制Fusion的DEVOVERRIDE模式启动,并在探测GAC之前搜索DEVPATH(及其子目录)。 MSDN上关于DEVPATHDEVOVERRIDE的FAQ将回答大多数有关使用此功能的问题。
Fusion (.NET的程序集加载器)仅按名称和版本搜索,将强命名程序集视为其他程序集的相同,不会在搜索DEVPATH之前搜索GAC,并简单地返回找到的第一个匹配项。您应该使用Fusion Log Viewer (fuslogvw)来查看是否正确启用了它,如此博客文章中所解释的DEVPATH
对于初次使用FusLogVw的用户,Scott Hanselman写了一篇优秀的介绍文章。查看器的界面有些过时,需要一点时间适应。
请注意,Fusion Log Viewer(或Assembly Binding Log Viewer,名称并无区别)将混淆地表示您使用了DEVOVERRIDE环境变量。它应该类似于这样:
LOG: Found assembly in DEVOVERRIDE path D:\testassemblies\Test.DLL

注意:如果您希望Visual Studio从DEVPATH位置加载程序集,您应该将注册表键设置为此位置,即设置(检查.NET版本键以匹配您的.NET版本):

[HKEY_CURRENT_USER\
    SOFTWARE\
    Microsoft\
    .NETFramework\
    v2.0.50727\AssemblyFoldersEx\
    DEVPATH]@="C:\SharedAssemblies"

在我的机器上,它仍然从GAC加载。它识别DEVPATH并在Fusion日志中记录它,但仍然不从那里加载程序集。有什么想法吗? DEVPATH和GAC中的程序集都已签名,并具有相同的标识。 - Turbo
@Turbo:我在PDB文件上发现了这篇文章,它还扩展到了关于DEVPATH的操作指南。我认为它与我上面提到的信息相符,只是它说(合理地)你必须确保DEVPATH可读/写。它还建议在应用程序级别上使用developerInstallation="true",这是个好消息(我还没有测试过)。 - Abel
@Turbo,还有一件事,也许你是指VS无法识别DEVPATH?我在更新的答案中考虑到了这一点,你还需要设置一个注册表值,以便VS能够找到该路径。 - Abel
谢谢提供的链接。我正在使用vstest.console.exe运行单元测试,并希望它从构建文件夹而不是GAC加载程序集。如pdb帖子中建议的那样,我为所有用户和应用程序包授予了读/写权限。我将DEVPATH设置为系统范围的环境变量,并在machine.config和vstest.console.exe.config中设置developerInstallation="true"。与以前一样,融合日志提到了DEVPATH值“LOG: DEVPATH = C:\MyPath”,但它没有在那里查找(融合表示它在GAC中找到了程序集,没有关于在DEVPATH中探测的信息)。 - Turbo
当DEVPATH未定义且developerInstallation为true时,我也会看到异常,这意味着Fusion知道它,但没有使用它。 - Turbo
@Turbo,我不确定是什么原因导致了这个问题。DEVPATH 的匹配只是按名称进行的,所以如果有相同的程序集,它应该能够找到。但是很可能 vstest.console.exe 使用了自己的程序集加载器,从而导致了不同的行为。您可以通过在构建和生产中使用不同的版本号来避免这种情况,以便 GAC 版本永远不会被找到,但是当然,这不是您想要的解决方案。我不使用 VSTEST 环境,我使用另一个环境,在那里我也发现了与默认 .NET 行为不同的程序集加载行为。 - Abel

3
Muse VSExtensions建议使用的旧版本配置是有效的!您可以在本地程序集中使用强名称: 请查看此页面: http://msdn.microsoft.com/en-us/library/7wd6ex19.aspx 基本上,在web.config中添加以下内容:
  <runtime>
    <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
      <dependentAssembly>
        <assemblyIdentity name="DllName"
          publicKeyToken="0123456789abc"
          culture="neutral" />        
        <!-- Assembly versions can be redirected in app, 
          publisher policy, or machine configuration files. -->
        <bindingRedirect oldVersion="2.0.0.0-2.5.0.0" newVersion="3.0.0.0" />
      </dependentAssembly>      
    </assemblyBinding>  
  </runtime>

如果我在GAC中有一个程序集,其版本可以从2.0.0.0到2.5.0.0,所有的调用都将被重定向到新版本(3.0.0.0)

在程序集部分,我添加了该程序集:

<add assembly="DllName, Version=3.0.0.0, Culture=neutral, PublicKeyToken=0123456789abc" />

就这样了。


1

要将一个版本重定向到另一个版本,请使用<bindingRedirect>元素。oldVersion属性可以指定单个版本或版本范围。例如,<bindingRedirect oldVersion="1.1.0.0-1.2.0.0" newVersion="2.0.0.0"/>表示运行时应该使用版本2.0.0.0而不是1.1.0.0和1.2.0.0之间的程序集版本。

s


我在哪里可以找到这个oldVersion属性? - Khaled Musaied

1

根据这篇答案中关于程序集加载顺序的摘录笔记: 如何防止.NET应用从GAC加载/引用程序集?

我猜测,在要求库作为程序集加载之前,调用本地DLL文件上的LoadLibrary,可能会将其在搜索顺序中向上移动。

遗憾的是,我不确定如何让您的LoadLibrary调用在框架开始加载引用的程序集之前运行。

因此,这只是一个想法,而不是完整的答案。


0

你可以使用 codebase 路径来代替使用 bindingRedirect。我有一个使用 MySQL.Data.dll 的工作示例。

<dependentAssembly>
    <assemblyIdentity name="MySql.Data" publicKeyToken="c5687fc88969c44d" culture="neutral" />
    <codebase version="5.2.5.0" href="/bin/MySQL.Data.dll" />        
  </dependentAssembly>

这可以在 Web 应用程序的 Web.config 中完成。


1
在查找GAC后,会检查CodeBase中的路径。因此,如果程序集在GAC中(如问题描述),则此方法将不起作用。 - Turbo

-1
为什么不将您喜欢的版本安装到全局程序集缓存 (GAC) 中,然后在 GAC 中引用它呢?

遗憾的是,旧版本由于某些未知应用程序仍在使用,因此无法从GAC_32卸载。更加遗憾的是,新版本与旧版本具有完全相同的版本号。将新版本安装在GAC_MSIL中并不能防止系统选择使用GAC_32旧版本。 - Jesse Chisholm

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