托管类库中与版本无关的引用依赖项

时间:2022-05-18 16:34:15

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.

我正在为log4net做一个appender,我面临的问题是如何管理我的类库建立的log4net版本的依赖关系与网站上部署的实际版本。我的类库必须引用log4net dll,因此它与我在构建时引用的版本绑定。但是,将部署此组件的站点将具有各种log4net版本,一些比我的旧,一些将更新。我该如何处理这个问题?我不想为每个log4net新版本发布我的appender的新版本,并且要在我的用户上正确匹配它们。我也不想让我的appender用户做复杂的并排清单技巧。我只是想将我的appender简单地复制到最终用户位置,并开始使用任何log4net版本。

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:

唯一可行的解​​决方案是使用清单。我测试了两个'自制'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>

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...).

其中publicKeyToken是真正的log4net程序集的实际密钥令牌,1.2.10是我的appender构建版本,newVersion是现场部署的版本。该部分可以添加到已部署的appconfig或webconfig中(也可以在机器配置中完成,但我不建议这样做......)。

9 个解决方案

#1


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).

很多项目都有你刚才描述的同样问题。据我所知,这不是你作为出版商可以控制的东西。您可以设置一个发布者策略,允许您在引用旧版本的程序集时自动指定应该使用某个版本的程序集,但是无法为不能控制的程序集指定此版本(如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.

在用户方面,管理员可以通过程序集重定向将旧版本的log4net(程序集可能引用)的请求指定为特定版本。

#2


You could handle the AssemblyResolve event:

您可以处理AssemblyResolve事件:

AppDomain current = AppDomain.CurrentDomain;
current.AssemblyResolve += current_AssemblyResolve;

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.

然后,您可以在Name属性(来自ResolveEventArgs)上使用字符串操作来删除版本号并加载程序集而不指定其版本。

#3


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?

在Visual Studio中选择对Log4Net的引用(假设您使用的是Visual Studio!)并显示属性。将“特定版本”设置为“假”,我认为你应该没问题。你有没有尝试过并遇到过问题?

#4


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.

根据您使用的接口,您可以使用实际为您实例化所需类的IoC容器(例如Castle,Spring.net等),如配置文件(或代码片段)中所定义。这样,您只使用接口,并且具体类的实际绑定由容器(框架)完成。如果你使用依赖注入,这是(尤其)可以实现的(参见Fowler关于它的文章)。只要您使用的接口保持不变,您就会对代码进行“后期绑定”。

#5


Simple and stupid solution will be to use proxy library written on vb.net.

简单而愚蠢的解决方案是使用在vb.net上编写的代理库。

I faced similar problem when was trying to build tool for MS OFFICE.

在尝试为MS OFFICE构建工具时,我遇到了类似的问题。

With VB I was not worrying about what version of office is installed.

使用VB,我并不担心安装了什么版本的办公室。

#6


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.

如果您依赖于可以安装在GAC中的库,那么您可以依赖它并且同时激活库的多个版本但是无论如何这对于log4net可能不会很好(鉴于共享配置状态模型通常使用)并增加了使用您的库的复杂性。

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.

可以指出,许多图书馆不应该输出任何类型的日志记录,因为他们无法很好地预测其用户的需求。 log4net的运行时可配置性确实在某种程度上缓解了这种情况。如果转移到使用框架内置的跟踪API,您的代码会更好吗?结果代码在依赖性方面变得更加轻量级,并且仍然允许在编译后进行一些复杂的调优。

#7


An alternate to my other answer. Hacky, and there may be complications but:

我的另一个答案的替代。 Hacky,可能会有并发症但是:

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.

将log4net代码构建到您的DLL中。由于它是开源的,并且在合理许可的许可下,您可以编辑文件(注意到您所做的)以更改可见性(以及可选的名称空间),以便它可用于您的代码并且不会干扰消费者。

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.

您可能希望添加一些代码以尝试从与应用程序其余部分相同的位置配置库,或者(并且更易于处理)允许从完全独立的文件/配置部分配置库日志。

#8


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.

对于CAD / CAM应用程序,我不得不面对这个问题,我们必须支持第三方机器的一些支持库。我做了什么我创建了两个程序集,一个链接到一个版本,另一个链接到另一个版本。我把它们放在同一个界面后面。

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.

诀窍是创建接口,以便两个程序集都可以无缝地工作。可能无法包装像Microsoft Office这样的复杂API。在这种情况下,您需要回溯使用API​​并查看最少使用的调用次数,并在此时构建您的界面。

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.

在您的情况下,您需要查看您在Log4net中使用的内容,看看是否可以围绕它构建一个接口。

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。正确设计界面然后您可以重新实现使用不同的包。此外,创建界面将精确定义应用程序与Log4net的交互方式。最后,如果Log4Net实现了突破性更改,那么您将有一条路径来处理它。

#9


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.

值得注意的是,由于log4net版本1.2.10.0和1.2.11.0之间的publicKeyToken更改,由于此处和此处讨论的原因,这两个版本之间几乎不可能发生此bindingRedirect。

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.

如果您使用NuGet,这更是一个问题,因为这意味着您需要绕过NuGet以使用可从此处下载的旧密钥版本的log4net。

However, I found that even this fails with the following exception for the reasons mentioned in the aforementioned link:

但是,我发现由于上述链接中提到的原因,即使是因为以下异常而失败:

Method not found: 'Void log4net.Config.BasicConfigurator.Configure()'

找不到方法:'Void log4net.Config.BasicConfigurator.Configure()'

#1


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).

很多项目都有你刚才描述的同样问题。据我所知,这不是你作为出版商可以控制的东西。您可以设置一个发布者策略,允许您在引用旧版本的程序集时自动指定应该使用某个版本的程序集,但是无法为不能控制的程序集指定此版本(如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.

在用户方面,管理员可以通过程序集重定向将旧版本的log4net(程序集可能引用)的请求指定为特定版本。

#2


You could handle the AssemblyResolve event:

您可以处理AssemblyResolve事件:

AppDomain current = AppDomain.CurrentDomain;
current.AssemblyResolve += current_AssemblyResolve;

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.

然后,您可以在Name属性(来自ResolveEventArgs)上使用字符串操作来删除版本号并加载程序集而不指定其版本。

#3


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?

在Visual Studio中选择对Log4Net的引用(假设您使用的是Visual Studio!)并显示属性。将“特定版本”设置为“假”,我认为你应该没问题。你有没有尝试过并遇到过问题?

#4


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.

根据您使用的接口,您可以使用实际为您实例化所需类的IoC容器(例如Castle,Spring.net等),如配置文件(或代码片段)中所定义。这样,您只使用接口,并且具体类的实际绑定由容器(框架)完成。如果你使用依赖注入,这是(尤其)可以实现的(参见Fowler关于它的文章)。只要您使用的接口保持不变,您就会对代码进行“后期绑定”。

#5


Simple and stupid solution will be to use proxy library written on vb.net.

简单而愚蠢的解决方案是使用在vb.net上编写的代理库。

I faced similar problem when was trying to build tool for MS OFFICE.

在尝试为MS OFFICE构建工具时,我遇到了类似的问题。

With VB I was not worrying about what version of office is installed.

使用VB,我并不担心安装了什么版本的办公室。

#6


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.

如果您依赖于可以安装在GAC中的库,那么您可以依赖它并且同时激活库的多个版本但是无论如何这对于log4net可能不会很好(鉴于共享配置状态模型通常使用)并增加了使用您的库的复杂性。

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.

可以指出,许多图书馆不应该输出任何类型的日志记录,因为他们无法很好地预测其用户的需求。 log4net的运行时可配置性确实在某种程度上缓解了这种情况。如果转移到使用框架内置的跟踪API,您的代码会更好吗?结果代码在依赖性方面变得更加轻量级,并且仍然允许在编译后进行一些复杂的调优。

#7


An alternate to my other answer. Hacky, and there may be complications but:

我的另一个答案的替代。 Hacky,可能会有并发症但是:

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.

将log4net代码构建到您的DLL中。由于它是开源的,并且在合理许可的许可下,您可以编辑文件(注意到您所做的)以更改可见性(以及可选的名称空间),以便它可用于您的代码并且不会干扰消费者。

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.

您可能希望添加一些代码以尝试从与应用程序其余部分相同的位置配置库,或者(并且更易于处理)允许从完全独立的文件/配置部分配置库日志。

#8


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.

对于CAD / CAM应用程序,我不得不面对这个问题,我们必须支持第三方机器的一些支持库。我做了什么我创建了两个程序集,一个链接到一个版本,另一个链接到另一个版本。我把它们放在同一个界面后面。

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.

诀窍是创建接口,以便两个程序集都可以无缝地工作。可能无法包装像Microsoft Office这样的复杂API。在这种情况下,您需要回溯使用API​​并查看最少使用的调用次数,并在此时构建您的界面。

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.

在您的情况下,您需要查看您在Log4net中使用的内容,看看是否可以围绕它构建一个接口。

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。正确设计界面然后您可以重新实现使用不同的包。此外,创建界面将精确定义应用程序与Log4net的交互方式。最后,如果Log4Net实现了突破性更改,那么您将有一条路径来处理它。

#9


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.

值得注意的是,由于log4net版本1.2.10.0和1.2.11.0之间的publicKeyToken更改,由于此处和此处讨论的原因,这两个版本之间几乎不可能发生此bindingRedirect。

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.

如果您使用NuGet,这更是一个问题,因为这意味着您需要绕过NuGet以使用可从此处下载的旧密钥版本的log4net。

However, I found that even this fails with the following exception for the reasons mentioned in the aforementioned link:

但是,我发现由于上述链接中提到的原因,即使是因为以下异常而失败:

Method not found: 'Void log4net.Config.BasicConfigurator.Configure()'

找不到方法:'Void log4net.Config.BasicConfigurator.Configure()'