我正在为 log4net 开发一个附加程序,我面临着如何管理我的类库构建的 log4net 版本与站点上部署的实际版本的依赖关系的问题。 我的类库必须引用 log4net dll,因此它与我在构建时引用的版本相关联。 然而,该组件将部署的站点将有各种 log4net 版本,有些比我的旧,有些比我的新。 我应该如何解决这个问题? 我不想为每个 log4net 新版本发布我的附加程序的新版本,并将正确匹配它们的负担置于我的用户身上。 我也不想要求我的附加程序用户执行复杂的并排清单技巧。 我只想将我的附加程序简单地复制到最终用户位置,并与那里存在的任何 log4net 版本一起开箱即用。
这是可以实现的吗? 我错过了一些明显的东西吗?
更新:
唯一可行的解决方案是使用清单。 我使用两个“自制”log4net 版本进行了测试,并添加以下配置部分解决了我的问题:
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="log4net"
publicKeyToken="..."
culture="neutral" />
<bindingRedirect oldVersion="1.2.10.0"
newVersion="..."/>
</dependentAssembly>
</assemblyBinding>
</runtime>
其中 publicKeyToken 是真实 log4net 程序集的实际密钥令牌,1.2.10 是我的附加程序构建的版本,newVersion 是当前的版本现场部署版本。 该部分可以添加到已部署的 appconfig 或 webconfig 中(也可以在计算机配置中完成,但我不建议这样做......)。
I'm working on an appender for log4net and I'm faced with the problem of how to manage dependencies on the log4net version my class library is built against vs. the actual version deployed on the site. My class library has to reference log4net dlls and as such it becomes tied to the version I'm referencing at built time. However, the sites this component will be deployed will have various log4net versions, some older than mine, some will be newer. How should I approach this problem? I don't want to release a new version of my appender for each log4net new version and place the burden of correctly matching them on my users. I also don't want to ask my appender users to do complex side-by-side manifest tricks. I just want my appender to be simply copied on the end user location and work out-of-the-box with whatever log4net version is present there.
Is this achievable? Am I missing something obvious?
Update:
The only working solution is to use the manifest. I tested with two 'homemade' log4net builds and adding the following configuration section solves my problem:
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="log4net"
publicKeyToken="..."
culture="neutral" />
<bindingRedirect oldVersion="1.2.10.0"
newVersion="..."/>
</dependentAssembly>
</assemblyBinding>
</runtime>
where the publicKeyToken is the actual key token of the real log4net assembly, 1.2.10 is the version my appender is built with and the newVersion is the currently deployed version on site. The section can be added to the deployed appconfig or webconfig (it can be done in machine config too, but I rather not recommend that...).
发布评论
评论(9)
许多项目都存在您刚才描述的相同问题。 据我所知,这不是你作为出版商可以控制的。 您可以设置发布者策略,该策略允许您自动指定当引用旧版本的程序集时,应使用程序集的某些版本,但无法为您无法控制的程序集(如 log4net)指定此版本。
在用户端,管理员可以通过 程序集重定向。
Lot of projects have the same problem that you've just described. As far as I know, this is not something that you as a publisher can control. You can set a publisher policy that allows you to automatically specify that a certain version of your assembly should be used when older versions of your assemblies are referenced, but there is no way to specify this for assemblies that you do not control (like log4net).
On your user side, the administrator can specify that requests for an older version of log4net(which your assembly might reference) by redirected to a specific version via an assembly redirect.
您可以处理 AssemblyResolve 事件:
然后可以对 Name 属性(来自 ResolveEventArgs)使用字符串操作来删除版本号并加载程序集而不指定其版本。
You could handle the AssemblyResolve event:
You could then use string manipulation on the Name property (from the ResolveEventArgs) to remove the version number and load the assembly without specifying its version.
编辑:下面的建议不起作用:特定版本在编译时应用,而不是执行时
在 Visual Studio 中选择对 Log4Net 的引用(假设您使用的是 Visual Studio!)并显示特性。 将“特定版本”设置为“false”,我认为您应该没问题。 您是否已经尝试过并遇到问题?
EDIT: The suggestion below doesn't work: Specific Version is applied at compile-time, not execution time
Select the reference to Log4Net in Visual Studio (assuming you're using Visual Studio!) and show the properties. Set "Specific Version" to "false" and I think you should be okay. Have you already tried that and encountered problems?
您可以使用 IoC 容器(例如 城堡, Spring.net< /a> 等),根据您使用的接口(如配置文件(或代码片段)中定义的那样)实际实例化您所需的类。
这样,您只需使用接口,而与具体类的实际绑定则由容器(框架)完成。 如果您使用依赖注入(另请参阅Fowler 的文章 )。
只要您使用的接口保持不变,您就可以“后期绑定”到您的代码。
You can use an IoC container (such as Castle, Spring.net, etc.) that actually instantiates the needed classes for you, according to the interfaces you use, as defined in a configuration file (or a code snippet).
This way, you only work with interfaces, and the actual binding with concrete classes is done by the container (framework). This is (especially) achievable if you work with dependency injection (see also Fowler's article about it).
You get "late binding" to your code, as long as the interfaces you use remain the same.
简单而愚蠢的解决方案是使用 vb.net 上编写的代理库。
当我尝试为 MS OFFICE 构建工具时,我遇到了类似的问题。
有了 VB,我就不用担心安装的是哪个版本的 Office。
Simple and stupid solution will be to use proxy library written on vb.net.
I faced similar problem when was trying to build tool for MS OFFICE.
With VB I was not worrying about what version of office is installed.
我非常不喜欢那些依赖“基础设施”库的库,例如单元测试框架、日志框架和代码生成工具,而它们的主要目的并未明确需要它。
如果您要依赖一个可以安装在 GAC 中的库,那么您可以依赖它并同时激活该库的多个版本,但这可能无法与 log4net 很好地配合(考虑到共享配置状态模型通常使用)并显着增加了使用库的复杂性。
人们可以指出,许多库不应该输出任何类型的日志记录,因为它们无法很好地预测用户的需求。 不过,log4net 的运行时可配置性确实在一定程度上缓解了这一问题。 如果您的代码改用框架中内置的 Tracing api 会更好吗? 生成的代码在依赖关系方面变得更加轻量级,并且仍然允许进行一些复杂的编译后调整。
I tend to intensely dislike libraries which take dependencies on 'infrastructure' libraries like unit testing frameworks, logging frameworks and code generation tools when their primary purpose does not explicitly require it.
If you were to depend on a library which could be installed in the GAC then you could rely on that and having multiple version of the the library active at once but this would likely not play well with log4net anyway (given the shared configuration state model commonly used) and adds significant complexity on using your library.
One can point out that many libraries shouldn't be outputting any kind of logging since they will be unable to anticipate the requirements of their users well enough. log4net's runtime configurability does mitigate this to some degree though. Would your code be better if it moved to using the Tracing api built into the framework? The resulting code becomes much more lightweight in terms of dependencies and still allows some sophisticated tuning post compile.
我的另一个答案的替代方案。 Hacky,可能会有些复杂,但是:
将 log4net 代码构建到您的 dll 中。
由于它是开源的,并且在合理许可许可下,您可以编辑文件(注意您所做的)来更改可见性(以及可选的命名空间),以便它可用于您的代码并且不会干扰消费者。
您可能希望添加一些代码来尝试从应用程序其余部分所在的同一位置配置您的库,或者(并且更容易处理)允许从完全独立的文件/配置部分配置您的库日志记录。
An alternate to my other answer. Hacky, and there may be complications but:
Build the log4net code into your dll.
Since it is open source and under a reasonably permissive licence you could edit the files (noting that you did) to alter the visibility (and optionally namespaces) so that it was usable for your code and didn't interfere with consumers.
You may wish to add some code to attempt to configure your library from the same location that the rest of the application does, alternatively (and much more tractable) allow configuration of your libraries logging from an entirely separate file/config section.
对于 CAD/CAM 应用程序,我必须使用我们必须支持的第三方机器的一些支持库来面对这个问题。 我所做的就是创建了两个程序集,一个链接到一个版本,另一个链接到另一个版本。 我把它们放在同一个界面后面。
然后在我们的设置对话框中您可以指定要使用的版本。 当然,如果使用了不正确的版本,就会出现错误。
诀窍是创建接口,以便两个程序集可以无缝工作。 可能无法包装像 Microsoft Office 这样的复杂 API。 在这种情况下,您需要回溯 API 的使用情况,查看哪里使用了最少的调用次数,并在此时构建您的界面。
就我而言,当我不得不将一张材料倾倒到机器上时,就到了关键时刻。 因此,我创建了一个界面,其中包含该机器的设置对话框例程和获取工作表对象的例程。 一切都位于该类中。
对于您的情况,您需要查看 Log4net 中使用的内容,看看是否可以围绕它构建一个界面。
这样做的一个优点是您不再受 Log4Net 束缚。 正确设计接口,然后您可以使用不同的包重新实现。 此外,创建接口将精确定义您的应用程序如何与 Log4net 交互。 最后,如果 Log4Net 实施了重大更改,那么您就有办法处理它。
For the CAD/CAM application I had to face this issue with some support libraries for a 3rd party machine that we had to support. What I did I created two assemblies one linked to one version and other linked to the other version. I put them both behind the same interface.
Then in our setup dialog you can specify which version to use. Of course if the incorrect version is used then a error would occur.
The trick is creating the interface so both assemblies can work seamlessly. It may not be possible to wrap a complex API like Microsoft Office. In which case you need to backtrack your use of the API and see where the minimum number of calls are used and build your interface at that point.
In my case the point came when I had to dump a sheet of material down to the machine. So I created a interface that had a routine the setup dialog for that machine and a routine that took my sheet object. Everything resided in that class.
In your case you need to look at what you use in Log4net and see if you can build a interface around that.
One advantage for doing this is that you no longer be welded to Log4Net. Design the interface correctly then you can reimplement use a different package. In addition creating a interface will precisely define how your application interacts with Log4net. Finally if Log4Net ever implements a breaking change then you have a path to deal with it.
值得注意的是,由于 log4net 版本 1.2.10.0 和 1.2.11.0 之间的 publicKeyToken 发生变化,由于讨论的原因,这两个版本之间的绑定重定向几乎是不可能的此处和此处< /a>.
如果您使用 NuGet,这甚至是一个问题,因为这意味着您需要绕过 NuGet 才能使用可从 此处。
但是,我发现即使这样也会失败,但由于 上述链接:
It's worth noting that, due to a publicKeyToken change between versions 1.2.10.0 and 1.2.11.0 of log4net, this bindingRedirect is nigh on impossible between those two versions for the reasons discussed here and here.
If you use NuGet, this is even more of a problem because it means you need to circumvent NuGet to use the
oldkey
version of log4net downloadable from here.However, I found that even this fails with the following exception for the reasons mentioned in the aforementioned link: