What is the best practice in the .NET world to manage transitive dependencies that cause version conflict ?
In details :
Project A Depends on Project B which in turn depends on library C
also
Project A also depends on Project X which depends on a DIFFERENT and (potentially) incompatible version of library C.
A->B->Cv1.0
&
A->X->Cv2.0
where Cv1.0 <> Cv2.0
Is there a way to make this work ?
Can it be done WITHOUT using the GAC ?
Can it be done even if B and X are in binary format only (source not accessible) ?
In other words is there a way where I can have Project B and X each using their own dependencies when used together in Project A without causing conflicts.
NOTE: I realize that ideally I should not have this problem at all but as reliance on external libraries expands this will be an unavoidable side effect. So I am wondering should it occur how best to deal with it.
There are a lot of similar questions On Stack Overflow.
For e.g. Referencing 2 different versions of log4net in the same solution
Summary:
Ensure that you deploy the assembly C in folders 1.0 and 2.0 respectively within the folder containing the main executable.
Change app.config file and include something like following:
<configuration>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="C" publicKeyToken="C's public key token" />
<codeBase version="version string for C1.0 e.g. 1.0.0.0" href="1.0\C.dll" />
<codeBase version="version string for C2.0 e.g. 2.0.0.0" href="2.0\C.dll" />
</assemblyIdentity>
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration>
you can get the public key token of C using sn -T C.dll
If v1.0 and v2.0 of C have different public key (though ideally they shouldn't) then include two dependentAssembly tags.
Related
I recently had my first encounter with Could not use file or assembly version issue. And I had some understanding How and Why assemblyBinding>
<configuration>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Runtime" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-7.0.0.0" newVersion="4.1.2.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration>
But I am still not sure about the usage of other versions info?
Q:- what is the use of Runtime version(red box), the File Version(yellow box) and any other use of Green box?
Noob Q:- Why can't we have one unique version for each DLL?
what is the use of Runtime version(red box)
This is the runtime that will be used by this reference. This will always be 4.0.30319 for .Net 4.8 references, and any other .Net framework 4.x reference. Since .Net framework 3.x and earlier is uncommon these days I do not think this is very useful anymore. I do not think this is even displayed for .Net standard/core projects.
the File Version(yellow box) and any other use of Green box
The file version, product version, and version in the reference box, all uses different fields in the file. So there is nothing that says they have to be the same. But if you just create a regular library project and build a dll, the compiler should set all of them to the same value. It is usually fairly simple with pure .Net dlls, but other types of dlls can have different versioning practices to make things more complicated.
Why can't we have one unique version for each DLL?
I'm not sure what exactly you mean by this. But versioning is difficult. A common problem occur when libraries use different versions of a third library. It is possible the two versions are different in some regards, and any way you can handle this risks breaking something.
I would recommend reading John Skeet's article on Options for .NET’s versioning issues for better insight into the potential problems.
This is not a problem question but a general understanding question on assembly binding redirect's working.
Queries
Why binding redirect shows only major version and not minor, build and revision numbers?
Does old and new version change only when there is change in major version?
<dependentAssembly>
<assemblyIdentity name="FooBar"
publicKeyToken="32ab4ba45e0a69a1"
culture="en-us" />
<bindingRedirect oldVersion="7.0.0.0" newVersion="8.0.0.0" />
</dependentAssembly>
Why are binding redirects needed at all? Suppose you have application A that references library B, and also library C of version 1.1.2.5. Library B in turn also references library C, but of version 1.1.1.0. Now we have a conflict, because you cannot load different versions of the same assembly at runtime. To resolve this conflict you might use binding redirect, usually to the new version (but can be to the old too). You do that by adding the following to app.config file of application A, under configuration > runtime > assemblyBinding section (see here for an example of full config file):
<dependentAssembly>
<assemblyIdentity name="C"
publicKeyToken="32ab4ba45e0a69a1"
culture="en-us" />
<bindingRedirect oldVersion="1.1.1.0" newVersion="1.1.2.5" />
</dependentAssembly>
You can also specify a range of versions to map:
<bindingRedirect oldVersion="0.0.0.0-1.1.1.0" newVersion="1.1.2.5" />
Now library B, which was compiled with reference to C of version 1.1.1.0 will use C of version 1.1.2.5 at runtime. Of course, you better ensure that library C is backwards compatible or this might lead to unexpected results.
You can redirect any versions of libraries, not just major ones.
We came across an issue with binding redirect for NewtonSoft.Json. We looked up the file version in win 10 file properties "9.0.1.19813", looked up the number and the redirect kept failing. Further investigation and found that we were looking at file version and not assembly version. So, I wonder if people are mistaking File Version (which changes often) and Assembly version (which you can't see in windows 10 File Explorer). To see the Assembly version of a dll you can run this in powershell. Replace the dll name with the one you want to find version for.
[Reflection.AssemblyName]::GetAssemblyName('C:\development\bin\Newtonsoft.Json.dll').Version
The result of above is.
Major Minor Build Revision
----- ----- ----- --------
9 0 0 0
See References:
How can i see the assembly version of a .NET assembly in Windows Vista and newer (WIndows 7, 2008)?
https://support.microsoft.com/en-nz/help/556041
This is not a problem question but a general understanding question on assembly binding redirect's working.
Queries
Why binding redirect shows only major version and not minor, build and revision numbers?
Does old and new version change only when there is change in major version?
<dependentAssembly>
<assemblyIdentity name="FooBar"
publicKeyToken="32ab4ba45e0a69a1"
culture="en-us" />
<bindingRedirect oldVersion="7.0.0.0" newVersion="8.0.0.0" />
</dependentAssembly>
Why are binding redirects needed at all? Suppose you have application A that references library B, and also library C of version 1.1.2.5. Library B in turn also references library C, but of version 1.1.1.0. Now we have a conflict, because you cannot load different versions of the same assembly at runtime. To resolve this conflict you might use binding redirect, usually to the new version (but can be to the old too). You do that by adding the following to app.config file of application A, under configuration > runtime > assemblyBinding section (see here for an example of full config file):
<dependentAssembly>
<assemblyIdentity name="C"
publicKeyToken="32ab4ba45e0a69a1"
culture="en-us" />
<bindingRedirect oldVersion="1.1.1.0" newVersion="1.1.2.5" />
</dependentAssembly>
You can also specify a range of versions to map:
<bindingRedirect oldVersion="0.0.0.0-1.1.1.0" newVersion="1.1.2.5" />
Now library B, which was compiled with reference to C of version 1.1.1.0 will use C of version 1.1.2.5 at runtime. Of course, you better ensure that library C is backwards compatible or this might lead to unexpected results.
You can redirect any versions of libraries, not just major ones.
We came across an issue with binding redirect for NewtonSoft.Json. We looked up the file version in win 10 file properties "9.0.1.19813", looked up the number and the redirect kept failing. Further investigation and found that we were looking at file version and not assembly version. So, I wonder if people are mistaking File Version (which changes often) and Assembly version (which you can't see in windows 10 File Explorer). To see the Assembly version of a dll you can run this in powershell. Replace the dll name with the one you want to find version for.
[Reflection.AssemblyName]::GetAssemblyName('C:\development\bin\Newtonsoft.Json.dll').Version
The result of above is.
Major Minor Build Revision
----- ----- ----- --------
9 0 0 0
See References:
How can i see the assembly version of a .NET assembly in Windows Vista and newer (WIndows 7, 2008)?
https://support.microsoft.com/en-nz/help/556041
I am working on a project that references dlls from another product. The product has a release each year and the assemblies version changes for each one, although the methods stay the same.
When I run a build of my project for 2010 when I try and run it for 2009 it throws an error because it is dependent on a different version. Is there a way around this?
If you're referring to a problem at runtime after swapping versions of your assembly without performing a rebuild of the program referencing your newly built assembly, you'll want to use a <bindingRedirect> directive to your program's App.config (or Web.config, if you're talking about a web site.)
bindingRedirect is used to instruct the .NET Framework that it's OK to use a version of an assembly other than the one the application was originally compiled against. By default, the CLR wants to see the same version of a DLL that your application was referencing during build, and if it doesn't it will throw an exception.
Try selecting the reference, and in property window set Specific Version as false.
It is possible to map different .net version of assembly in app.config that you put in application root folder
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="Waters.ACQUITY.Remote"
publicKeyToken="6c13fd0b3604de03"
culture="neutral" />
<bindingRedirect oldVersion="1.40.0.0"
newVersion="1.60.0.0"/>
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration>
This is solution when assembly you have referenced has references inside it to another specifc library version.
It happens when at compilation time "Specific version" is set to true. To avoid this problem it should be false.
If A references assembly B 1.1 and C, and C references B 1.2, how do you avoid assembly conflicts?
I nievely assumed C's references would be encapsulated away and would not cause any problems, but it appears all the dll's are copied to the bin, which is where the problem occurs.
I understand the two ways around this are to use the GAC or assembly bindings? The GAC doesn't seem like the best approach to me, as I don't like assuming dlls will be there, I prefer to reference dlls from a lib directory in my solution.
Where as assembly bindings don't seem robust to me, what if one version of the assembly has functionality that the other doesn't, will this not produce problems?
In my case its because I'm using a 3rd party dll uses a older version of nHibernate, than I'm using myself.
I've achieved the same results using the GAC in the past, but you should question your reasons for having to reference more than one version and try to avoid it if possible. If you must do it, a binding redirect may help in your case.
Also, have you read this yet?
A seemingly little known way of doing this is to use the extern keyword.
From C# Reference
To reference two assemblies with the same fully-qualified type names,
an alias must be specified at a command prompt, as follows:
/r:GridV1=grid.dll
/r:GridV2=grid20.dll
This creates the external aliases GridV1 and GridV2. To use these
aliases from within a program, reference them by using the extern
keyword. For example:
extern alias GridV1;
extern alias GridV2;
Each extern alias declaration introduces an additional root-level
namespace that parallels (but does not lie within) the global
namespace. Thus types from each assembly can be referred to without
ambiguity by using their fully qualified name, rooted in the
appropriate namespace-alias.
In the previous example, GridV1::Grid would be the grid control from
grid.dll, and GridV2::Grid would be the grid control from grid20.dll.
I was required to support multiple versions of an assembly and found this solution:
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="MyAssembly" publicKeyToken="..." />
<codeBase version="1.1.0.0" href="MyAssembly_v1.1.0.0.dll"/>
<codeBase version="2.0.0.0" href="MyAssembly_v2.0.0.0.dll"/>
</dependentAssembly>
</assemblyBinding>
</runtime>
You can add a bindingRedirect element to your configuration file to specify which version of the assembly you want to use at runtime.
<configuration>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="myAssembly"
publicKeyToken="32ab4ba45e0a69a1"
culture="neutral" />
<bindingRedirect oldVersion="1.0.0.0"
newVersion="2.0.0.0"/>
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration>
The .NET runtime is perfectly capable of loading multiple versions of the same assembly simultaneously. If you are going to open this can of worms, however, I strongly suggest you stronly name your assemblies and use the Major.Minor.* naming scheme to avoid naming conflicts.
I don't think you should think of a one-size-fits-all approach to using (or not) the GAC. The GAC can be really nice if you want to automagically use new functionality published with future versions of a DLL. Of course, this blessing comes at a cost that new versions might not work exactly like you expect them too :). It's all a matter of what's most practical, and how much control you have over what gets published to the GAC.
Regards,
-Alan.