Where assemblies are loaded from? - c#

We have an azure service. The solution for it includes several projects. Some of projects references azure.storage ver. 4.2.1. Based on nugget.org the dependencies for azure.storage are odata, edm etc >= 5.6.0. We want to use the latest one 5.6.3. So we added to the projects references references to all dependent assemblies from version 5.6.3 (despite that ILSPY shows that the dependencies are for ver 5.6.0). But unfortunately the loaded dlls are from gac with version 5.6.0. (not from the path we referenced with the latest version)
How the dlls are loaded ?:
Based on Force load an assembly from the /bin and not the GAC? and http://msdn.microsoft.com/en-us/library/yx7xezcf.aspx - it loads assemblies from GAC firstable
Based on http://blogs.msdn.com/b/manishagarwal/archive/2005/09/28/474769.aspx and Microsoft.Common.CurrentVersion.targets it should look first in bin directory - not gac.
What is correct? And how (it at all) can I achieve what I try to do?

You are comparing the resolving of an assembly for compilation to the resolving of assemblies on execution.
Both your points and their references are true, but only for their specific situation. Your #2 references explains the compilation process assembly resolving, your #1 references talk about the execution process.

Related

How to build and configure .net assembly so it ignores version of referenced DLL at runtime?

I am building a DLL in C# that references another DLL. I don't want the DLL that I am building to care about which version of the referenced DLL is on the customer's computer at runtime (note: I do not ship the referenced DLL to them, only the one that I build). It turns out that they have a newer version of the referenced DLL on their machine and when they tried to execute the code in my DLL they got the following error:
Exception calling ".ctor" with "0" argument(s): "Could not loadfile or assembly ', Version=, Culture=neutral
In case it helps to know, the Specific Version property on the referenced DLL is set to False in my Visual Studio. How can I build my DLLs so that it won't complain about the version of the referenced DLL that the customer has deployed on their machine?
When you build an assembly the dll version of referenced assembly is written into your assembly. Generally, and without any configuration, it will have no troubles if you replace originally referenced assembly with the one of higher version, where only first 3 numbers matter. 1.1.1.x - x doesn't matter. And as long as "replacement assembly" contains API that your code may call. It seems that you have exactly that issue - a different constructor in the version you using that wasn't present in original assembly.
For making your assembly work with various versions of referenced dlls you can try to tweak app.config to set version range for this dll. Read about redirecting assembly version.

What causes two versions of the same assembly to be referenced by another assembly

I have a C# assembly with a few Nuget dependencies that is being produced as an artifact from a TFS build. The resulting assembly has two different versions of the same assembly being included in its referenced assembly list. I'd like to understand how this occurs since I'm unsure of the consequences and didn't think it was possible in the first place. I've been unable to reproduce the problem in any kind of minimal example so far.
To elaborate, I have Company.Product.A.dll which according to .NET Reflection, Mono.Cecil, dotPeek, and ILSpy references both Company.Product.B.dll 1.0.0.0 and Company.Product.B.dll 1.0.2.0 e.g.
// The list of referenced assemblies below contains two versions of Company.Product.B.dll
Assembly.LoadFile(#"C:\Company.Product.A.dll").GetReferencedAssemblies()
The underlying .csproj for Company.Product.A.dll has a single reference to Company.Product.B.dll 1.0.2.0 and an appropriate Nuget package reference to the same version.
The Company.Product.A.dll assembly is dependent on another assembly that also references Company.Product.B.dll (I'm not sure this is significant but I'm suspicious that it might have something to do with the problem if the other assembly references an older version of Company.Product.B.dll).
I'm not aware of any ILMerge malarkey going on during the build process but I'm still looking into that just in case.
The build server is producing clean builds each time.
Neither the root assembly nor the two versions of the dependent assemblies are strong named.
There appears to be no negative consequence of the spurious assembly reference; the product that uses the assemblies works as intended.
The ILDasm manifest is shown below (I'm unsure what the _8 on the second reference means):
.assembly extern /*23000008*/ Company.Product.B
{
.ver 1:0:2:0
}
.assembly extern /*23000009*/ Company.Product.B as Company.Product.B_8
{
.ver 1:0:0:0
}
Can anyone tell me what sequence of events would lead to one assembly having references to two different versions of the same dependent assembly?

Is "Copy Local" transitive for project references?

Wrt. the proposed dupe: Since this here queston suggests the opposite of the linked question, I'd rather like to think it is not a dupe.
First, I did read What is the best practice for “Copy Local” and with project references? (also this) and I'll have to try this out anyway, but getting general feedback on this seems necessary as the docs on this stuff are horrible and I'm only on VS2010 and maybe they changed something in newer versions that'll be nice to know.
Second, I'm only interested in project references for this question as I've read that assemblies from the GAC are handled differently and the GAC is irrelevant for my problem.
Third, after reading the suggested dupe, but more so the nice answer here by #Albireo, it would also appear that it is important to differentiate file dependencies, where the dependency references a dll assembly file and project dependencies (i.e. what I'm asking about), where the dependency references a project and implicitly the output file of that project.
Anyway, here's the situation, somewhat peculiar I think, but still:
2 C# executable projects
n C# dll assembly projects
The 2 executables have different output directories as they will be deployed separately and that way they're also separate on the developer machine
The 2 executables have dependencies on some of the DLL assemblies (which may depend on each other)
There are three output directories:
/x1 for executable 1 project
/x2 for executable 2 project
/lib for all the dll assemblies
The DLL assemblies all have Copy Localset to false for their project references, as they all build to the same output directory.
The 2 executable projects have set Copy Local to true for all the DLL assembly project references they reference directly, so that the DLLs will be copied into /x1 /x2 respectively.
The question now is wrt. to DLLs that are not directly referenced by an executable project, but only transitively through a referenced assembly: Will assemblies, that are only referenced transitively through another assembly, be copied into the output folder of the executable, when "Copy Local" is set to true on the first assembly?
Example:
x1.csproj (e.g.Output = x1/one.exe)
Reference: dlA.csproj ( e.g. Output = lib/a.dll) with Copy Local = *true*
(no direct reference on b.dll)
dlA.csproj ( e.g. Output = lib/a.dll)
Reference: dlB.csproj ( e.g. Output = lib/b.dll) with Copy Local = **false**
(no direct reference on c.dll)
dlC.csproj ( e.g. Output = lib/c.dll)
(no further relevant references)
Thus, we have a logical dependency of one.exe -> a.dll -> b.dll -> c.dll, where only a.dll with obviously be copied to the output directory of one.exe. Will the other two dlls also be copied to the output directory? Is this documented somewhere?
And, yes, I tried it. And, yes, it seems to work, but I haven't poked it hard enough yet and anyway there maybe something more to it that I may have missed. (And also there's the question wrt. any official docs.)
it would also appear that it is important to differentiate file dependencies, where the dependency references a dll assembly file and project dependencies (i.e. what I'm asking about), where the dependency references a project and implicitly the output file of that project.
Not really, no.
MSBuild doesn't really care if the reference points to another project in the solution or to a DLL.
If ProjectA depends on ProjectB to build ProjectA ProjectB must be already built (and up-to-date), MSBuild will then pull its DLL (not its C# code) and link it to ProjectA.
Adding a project reference instead of a DLL is "syntactic sugar" for your convenience: this way MSBuild knows it must pick the output of the referenced project, whatever the output is.
Otherwise, you'll have to manually pre-build the dependency, find its DLL and link it to the project, repeating the process whenever you switch build configuration, move or rename things. Not really practical.
Will the other two dlls also be copied to the output directory?
If any kind of element from a dependency is used directly from the project where the assembly is referenced, that reference will be copied.
An example could be this solution layout:
MySolution
MySolution.ConsoleApplication
MySolution.FirstDependency
MySolution.SecondDependency
MySolution.ThirdDependency
MySolution.FourthDependency
With this dependency chain:
MySolution.ConsoleApplication
MySolution.FirstDependency
MySolution.SecondDependency
MySolution.ThirdDependency
MySolution.FourthDependency
If you build this solution you'll notice that in MySolution.ConsoleApplication output directory there will be the DLLs for MySolution.FirstDependency, MySolution.SecondDependency and MySolution.ThirdDependency but no DLL for MySolution.FourthDependency.
Why is it so? When MSBuild builds MySolution.SecondDependency it notices that there's a dependency declared to MySolution.FourthDependency, but since it can't find any usage of any kind of element from MySolution.FourthDependency in MySolution.SecondDependency code it decides to perform some "optimization" and omits MySolution.FourthDependency assembly from the output.
This same issue bit me in the past when I added through NuGet AutoMapper to a "deep dependency": adding AutoMapper adds two assembly references, AutoMapper and AutoMapper.Net4, where the second assembly is loaded by the first through reflection when it needs to perform certain kind of action on the new collection objects introduced by the .NET Framework 4. Since the second assembly is loaded through reflection MSBuild thinks it's unused and doesn't bother to copy it around.
So, yes, they will be copied as long as you're using them directly and not through reflection.
Is this documented somewhere?
This behavior seems to be a "feature" of MSBuild, I managed to find a blog post by some folks from Microsoft back when I experienced this issue, but I can't find it again at the moment.
It is very straight forward, doesn't have anything to do with Copy Local. MSBuild looks in the metadata of an assembly to see what the dependencies are for an assembly. So can you, run ildasm.exe on the assembly and double-click the Manifest. Be sure to try this to get insight. You'll see the .assembly directives. Inserted by the compiler when it built the assembly, only the referenced assemblies you actually used in your code will be listed.
If MSBuild can find such an assembly in the same directory then it will automatically copy it. If not then it will silently skip the copy.
From this, you can deduce the failure modes. It cannot copy unmanaged DLLs, they do not appear in the metadata. It cannot copy assemblies that you have an indirect dependency on through Assembly.Load/From(), they don't appear in the metadata either. It cannot copy assemblies that haven't been built yet, a build order problem. And it cannot copy assemblies whose Copy Local property you set to False. Which is normally only a valid choice if the assembly is present in the GAC, no copy required.
For such cases you need to help, XCOPY in a post-build event gets the job done.

How to reference and provide assemblies of different versions without using the GAC

I have a project P that references two dlls say A.dll and B.dll. A.dll in turn references version 1.0.0.0 of X.dll and B.dll refences version 2.0.0.0 of X.dll.
So when I run the code of P I need to to have both versions of X available for it. For certain reasons I don't want to GAC either. So I thought to simply put them in the bin folder, but with different file names: X.dll and X.v2.dll. However, this results in that when running P I get this error:
Could not load file or assembly X.v2.dll or one of its dependencies.
The located assembly´s manifest definition does not match the assembly
reference.
I take it this is due to that, when running code that pulls in A.dll which then wants to load version 1 of X, an attempt to load X.v2.dll is done prior to loading X.dll, thus throwing the exception.
Is this assumption correct? And how do you deal with the need to make available two versions of an assembly without putting them in the GAC?
(Both A.dll and B.dll, X.dll and X.v2.dll are strongly named)

Where should you store 3rd party assemblies?

Okay, so we have a rather large solution with about 8 different projects inside it. Each of these projects depend on various different 3rd party assemblies. This solution is in the trunk branch of source control. We also have about 5 different branches off of trunk.
What is the best way to manage these 3rd party assemblies? When you add a reference to an assembly and then click it and view the properties window I notice that it has a hard coded path to the assembly.
For example: All our branches are mapped to "C:\Code\". So trunk would be "C:\Code\Trunk" and a branch would be "C:\Code\somebranch".
If I create a folder in "C:\Code\Trunk" called "Assemblies" and then drop all our 3rd party assemblies in that folder, and then I add a reference to an assembly in there is that assembly reference relative? If I click the added assembly I see the grayed out path property says "C:\Code\Trunk\Assemblies\someassembly.dll".
What happens if I then branch off of trunk? Would "somebranch" still have a reference to "C:\Code\Trunk\Assemblies\someassembly.dll" or would it then reference "C:\Code\somebranch\Assemblies\someassembly.dll"?
Currently we actually have a branch in source control called "Assemblies" which is mapped, just like any other branch, to "C:\Code\". So all branches with projects referencing assemblies have references to "C:\Code\Assemblies\someassembly.dll" no matter which branch the project is in, the path would be the same.
Unfortunately this means that you have to get the latest version of the branch you are working in AND the assemblies branch in order to get the solution to build successfully.
To sum it all up:
How do you add a reference that is relative to the solution? (i.e. Add a reference to C:\Code\Trunk\Assemblies\someassembly.dll and have that path be relative to the project that added it, so that when creating a branch it references the branched assemblies folder and not trunk's assemblies folder. Or is this reference already relative?
What are other recommended strategies for managing 3rd party assemblies?
Now we have nuget you can use it for all supported oss packages and even create your own nuget packages for other 3rd party assemblies. It is worth mentioning openwrap as an alternative to nuget.
nuget stores packages at solution level
so each branch (and trunk) would keep a version of these.
I'd suggest this is preferable behaviour. You would want to keep your assemblies version separate if upgrading a 3rd party for example.
In the past I've used svn's externals command to build the specific version from the internally developed dependencies. There's no reason you couldn't stick those in a repository and use externals (or you scm's equivalent) to get the right version.
I've used build events to get the dlls into the right place too.
Yes use an assemblies folder off of the trunk. I like the name lib better then assemblies.
Yes the path is already relative. When you branch your projects will get the correct assemblies folder.
Depending on how many third party assemblies you are using you make also want to organize your assemblies folder so it is not one big mess of dlls.
We have a SolutionItems folder in our solution for the 3rd party references.
Every branch of the solution has it's own copy.
When we add a reference we use the 'Browse' tab in the add reference dialog and select the assembly relative to our current project.
The Project file contains this:
<Reference Include="SomeAssembly, Version=0.1.0.0, Culture=neutral, PublicKeyToken=8xxxxxxxxxxx, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\Solution Items\SomeAssembly.dll</HintPath>
</Reference>
I usually create a common project that all the others reference. Inside that common project, I create a folder called deps (for dependencies). Each of the other projects then references the copy of the DLL in the common project's deps folder.

Categories

Resources