I have been working on upgrading my application to use the MongoDB 2.0 driver, but have hit a road block since MongoDB is not currently releasing a strongly named version of the assemblies. As a result I cannot build an assembly in my solution that gets registered in the Global Assembly cache (GAC) because GAC requires assemblies to be strongly named.
Using the technique explained on Ian Picknell's and Ryan Farley's Blogs I was able to strongly name the assemblies; MongoDB.Bson.dll, MongoDB.Driver.dll, and MongoDB.Driver.Core.dll. After strongly signing them I removed the old references to these assemblies and added references to the new strongly named versions. When I try to build the project in VS2013 2 errors(see below) are generated both of which are trying to reference the weakly named assembly (This is my understanding because the error states PublicKeyToken=null).
I've also tried building strongly named assemblies straight from the source code since MongoDB is open source but that has been generating hundreds of errors that i'm still working through.
Error 1 The type 'MongoDB.Bson.BsonDocument' is defined in an assembly that is not referenced. You must add a reference to assembly 'MongoDB.Bson, Version=2.0.1.27, Culture=neutral, PublicKeyToken=null'.
Error 2 The type 'MongoDB.Driver.IAsyncCursorSource`1<T0>' is defined in an assembly that is not referenced. You must add a reference to assembly 'MongoDB.Driver.Core, Version=2.0.1.27, Culture=neutral, PublicKeyToken=null'.
I Installed the nuget package StrongNamer and it signed automatically the unsigned dependencies.
Strong Namer will automatically add strong names to referenced
assemblies which do not already have a strong name. This will allow
you to reference and use NuGet packages with assemblies which are not
strong named from your projects that do use a strong name.
Read topic 331520 and Dirk Vollmar's answer (Also give him an upvote). The link he provides explained the problem I was encountering.
A quick summary of the problem was the original MongoDB.Driver.dll and MongoDB.Driver.Core.dll were built with a reference to the unsigned version of MongoDB.Bson, so I had to update the references inside of those DLL's to include the publickeytoken I used to sign the assemblies. All is explained by the .NET-fu article under the Foo, Bar, Baz section.
Related
Suppose you have an assembly B that references A and you have the source code for the assembly A only. Is it possible to build from source code the assembly A and debug it?
Currently we get this error:
Could not load file or assembly 'name' or one of its dependencies. The
located assembly's manifest definition does not match the assembly
reference.
Is there any way to bypass it?
This will depend on if the original assembly A that is referenced is strongly named. This is a feature where assemblies are signed. Keys for all compile time references are stored in the built assembly, B in your case. When loading assemblies the loader may then verify the signatures of all references to ensure the correct assembly is loaded.
So if strong naming is used it is not easy to replace the assembly A with a newer version without recompiling B. There is however a strong name validation bypass feature for full trust application domains.
If you manage to bypass or disable the strong naming you should simply be able to replace the file in the directory with the new version and attach visual studio.
I have been looking for an answer to that but so far I have not been able to. For those who are not familiar with the basis of my question, here is some explanation to that.
Context
You can have two types of assembly in .NET when it comes to naming your assemblies:
Strong-named Assemblies
Simple-named Assemblies (aka Weakly-named Assemblies as coined by Jeffrey Richter in his CLR via C# book).
Unlike Simple-named Assemblies, the Strong-named assemblies are signed with a public/private key. Related to my question, one of the benefits of signing the assembly is the following:
Now the version number of your assembly becomes important. Assemblies
contain a static reference to other assemblies that they use the types of. When they reference a strong-named assembly, you must give them
that same exact assembly with the exact signature (name, version,
culture, and public key token). Otherwise, the fusion assembly loader
will fail at resolving it.
Also, strong-named assemblies can only reference other strong-named assemblies but not simple-named assemblies.
Question
Based on what I have described, I also expect the same rules to be applied to referencing .NET Framework assemblies. Meaning that my assemblies will search for exact versions of the referenced assemblies that are shipped with a .NET Framework that I originally used the assemblies of in my compiled application. However, when you upgrade from .NET 4.5 to 4.7, without re-compiling your application, your application is still working and supposed to be working. How is that possible and how does Microsoft manages that? Do they apply an exception to their assemblies in the fusion loader so that it always loads the latest .NET Framework assemblies for user-developed applications? Does it use a bindingRedirect internally for each possible old version to new version? Do they use publishers policies?
I feel like the answer is hidden in this text:
The runtime uses the following steps to resolve an assembly reference:
Determines the correct assembly version by examining applicable
configuration files, including the application configuration file,
publisher policy file, and machine configuration file. If the
configuration file is located on a remote machine, the runtime must
locate and download the application configuration file first.
... -- Microsoft Doc
Other notes:
Specific Version
For some, there is a misconception about Specific Version = true flag, which can be changed in Visual Studio when referencing an assembly. This flag is for compile-time checking only and has nothing to do with the runtime assembly resolution. In fact, the assembly will still have the strong name including assembly name, version, and public key token (if exists).
An Extra Quick Question: By Default?
By default, the runtime attempts to bind with the exact version of an
assembly that the application was built with.
says, Microsoft. It confuses me a little bit. Does it mean that version number is important for both weakly-named and strong-named assemblies to get resolved? For instance, if I drop a weakly-named assembly with an incremented version number, then will it not be resolved by other assemblies that are referencing the older version of it?
So, I have two projects... One is a big class library, and the other is a big simulator project.
Originally during testing the assemblies were not strong-named, but now that I need COM visibility, I need to strong name them (I need this quick, and it is easier to make them COM visible rather than deal with the CLI).
I generated a .snk and added it to the properties of each of the projects, the class library compiles just fine and outputs the .dll just fine. I added this .dll as a reference in the simulator project, but now the simulator project is giving me the following error:
The type "DynamicsControl" is defined in an assembly that is not
referenced. You must add a reference to assembly "DynamicsControl,
Version=0.4.3.0, Culture=neutral, PublicKeyToken=null"
The type DynamicsControl does exist in the class library (I verified), but it is claiming I am not loading the correct assembly. I believe it is the PublicKeyToken=null that is causing the issue (as my assembly would have a public key).
When I do sn -Tp DynamicsControl.dll (the one in the location I am referencing), it outputs the private and public key, so I know it has a proper signature.
What step am I missing? Do I have to add the assembly to the GAC? Do I manually have to add the public key?
Yes you need to specify the publicKeyToken for a strong named assembly otherwise it will look for unsigned version of the assembly. You can find out the public key token either by adding the assembly to GAC (and you will see it there) or by using the steps mentioned in the this link
If you want to read more about strong name and dll hell you can go to this URL .
Strong name prevents from dll hell which means you can have multiple versions of same assembly.
To your other question you don't have to add the assembly to GAC but CLR will look for the assembly first in GAC and then other places. Also, you don't have to add the key manually. If you remove the reference and add it back using the signed assembly it should automatically take the key.
I have created a test project whose output will be strong named assembly. I have specified the SNK file name in the 'project -> properties - > Signing' section so that assembly will be strong named.
I added reference of the assembly which is NOT signed or strong named. I was expecting that my project will not at all build since it's referencing unsigned assembly. However, it built and i could install the assembly in GAC too! Why is it so? (I'm using .NEt 4.0 and it's gacutil.exe)
Note: I have ensured that the assembly which is being referenced is NOT strong named.
Can i programatically load the 'unsigned' (i.e. non-strong named) assembly in my program whose output is going to be strong named assembly? If yes, why is it allowed to do so when on the other hand, .NET don't allow to add reference to unsigned assemblies?
If I have multiple versions of the same assembly in GAC. In my configuration file, If I specify just the name of the assembly, which version will be loaded from GAC? Will it be latest version everytime? Which API will be useful? I found many methods marked 'deprecated' in Assembly class.
You can reference the unsigned assembly in VS.Net from a signed assembly. But if you call anything from the signed to the unsigned, the build will fail.
There is no problem in signed assembly referencing an unsigned assembly. It can be done and signed assembly can be GACed.
Yes, you can load.
How can you specify assembly by just name ? There is only one API LoadWithPartialName and thats deprecated for many reasons. You need version, culture and public key token to uniquely identify assembly.
I am not a .NET developer, so there might be some basic things I don't know.
I have some experience coding in C#, but now I have a question. One of my projects (A) references another ptoject (B), with "local copy" set. When B.dll is in the same location as A.exe everything works. But when B.dll is put in a common directory from PATH it doesn't work.
One of my coworkers said he thought I should make B strongly signed. Is he correct? Is that why one would strongly sign an assembly?
I read a bit about in in the internet but all I saw was about security... If so, how does one sign an assembly and what consequences does it have? Please note that I am using VS2003 .Net 1.1.
Edit: Thank you all very much for your answers, however all the links you provided refer to later versions of VS and .NET which have some sort of Signing tab in project properties. Does anybody know (or give a link )how to strongly name the assembly in VS2003 .Net1.1?
Your problem is not related to assembly signing in the first place. .NET does not use the PATH environment variable to load assemblies. The process is actually a bit more complex and you best read all details in MSDN (also see steps 1 to 4):
How the Runtime Locates Assemblies
In your case it might be the best to install the shared assembly to the GAC. Installing to the GAC requires that your assembly has a strong name, so this is probably what your co-worker referred to.
Update:
As you asked specifically about strong-naming a .NET 1.1 assembly I'd suggest checking out the following question:
How to give a .NET 1.1 dll a strong name in VS2003
I think that what your co-worker might be referring to is "Strong Naming" an Assembly.
Strong Naming is what enables you to deploy your assembly to the GAC.
Once it is in the GAC, then any application using that assembly can always locate it. Path's are irrelevant and that is the preferred way to have shared assemblies deployed.
To strong name an assembly, you can use the sn.exe tool that comes with Visual Studio to generate a strong name and then sign the assembly using the keyfile that is generated via sn.exe.
EDIT : Example of how to use SN.exe to strong name an assembly is here
Also, I think you should understand how the runtime loads assemblies. From MSDN
The runtime uses the following steps to resolve an assembly reference:
Determines the correct assembly version by examining applicable
configuration files, including the application configuration file,
publisher policy file, and machine configuration file.
If the configuration file is located on a remote machine, the
runtime must locate and download the application configuration file
first.
Checks whether the assembly name has been bound to before and, if so,
uses the previously loaded assembly.
Checks the global assembly cache. If the assembly is found there, the
runtime uses this assembly.
Probes for the assembly using the following steps: If configuration
and publisher policy do not affect the original reference and if the
bind request was created using the Assembly.LoadFrom method, the
runtime checks for location hints.
If a codebase is found in the configuration files, the runtime checks
only this location. If this probe fails, the runtime determines that
the binding request failed and no other probing occurs.
Probes for the assembly using the heuristics described in the probing
section. If the assembly is not found after probing, the runtime
requests the Windows Installer to provide the assembly. This acts as
an install-on-demand feature.
Note: There is no version checking for assemblies without strong
names, nor does the runtime check in the global assembly cache for
assemblies without strong names.
The right way to do this is by deploying your .dll in the GAC. http://support.microsoft.com/kb/815808
what is the reason you want to put the B.dll in a common directory? is it because it can be used by a another program? if so adding it to the GAC is the best option. See this one
As 0xA3 already mentioned you should read the article at MSDN. But what is not so good explained in the article is the usage of the AssemblyResolve event. It will be thrown if the Framework didn't find the assembly at any place, givin you a chance to start a search on yourself (maybe in your common folder) and return the needed assembly.
An example on how to use this, can be found in my question here.