tags:

views:

3520

answers:

4

%windir%\Microsoft.NET\assembly\ is the new GAC, does it mean now we have to manage two GACs, one for .NET 2.0-3.5 apps and the other for .NET 4.0 apps?

The question is, why?

+21  A: 

Yes since there are 2 distinct Global Assembly Cache (GAC), you will have to manage each of them individually.

In .NET Framework 4.0, the GAC went through a few changes. The GAC was split into two, one for each CLR.

The CLR version used for both .NET Framework 2.0 and .NET Framework 3.5 is CLR 2.0. There was no need in the previous two framework releases to split GAC. The problem of breaking older applications in Net Framework 4.0.

To avoid issues between CLR 2.0 and CLR 4.0 , the GAC is now split into private GAC’s for each runtime.The main change is that CLR v2.0 applications now cannot see CLR v4.0 assemblies in the GAC.

Source

Why?

It seems to be because there was a CLR change in .NET 4.0 but not in 2.0 to 3.5. The same thing happened with 1.1 to 2.0 CLR. It seems that the GAC has the ability to store different versions of assemblies as long as they are from the same CLR. They do not want to break old applications.

See the following information in MSDN about the GAC changes in 4.0.

For example, if both .NET 1.1 and .NET 2.0 shared the same GAC, then a .NET 1.1 application, loading an assembly from this shared GAC, could get .NET 2.0 assemblies, thereby breaking the .NET 1.1 application

The CLR version used for both .NET Framework 2.0 and .NET Framework 3.5 is CLR 2.0. As a result of this, there was no need in the previous two framework releases to split the GAC. The problem of breaking older (in this case, .NET 2.0) applications resurfaces in Net Framework 4.0 at which point CLR 4.0 released. Hence, to avoid interference issues between CLR 2.0 and CLR 4.0, the GAC is now split into private GACs for each runtime.

As the CLR is updated in future versions you can expect the same thing. If only the language changes then you can use the same GAC.

Brian R. Bondy
That blog post merely restates the OP's discovery, it doesn't explain *why* the GAC needed to be split. It isn't obvious, the original GAC would have been quite capable of keeping the 4.0 assemblies separate. They have a new [AssemblyVersion]
Hans Passant
@Hans: Maybe not the exact reason but it does say: "To avoid issues between CLR 2.0 and CLR 4.0", also the question had 2 questions inside of it. The second question being: "does it mean now we have to manage two GACs, one for .NET 2.0-3.5 apps and the other for .NET 4.0 apps?"
Brian R. Bondy
deleting my post as it is also linked to the same article
Mahesh Velaga
@Hans Passant: I added some more info.
Brian R. Bondy
Thanks a lot for the links. I cannot mark as answer because you have some misconceptions, e.g. "It seems that the GAC has the ability to store different versions of assemblies as long as they are from the same CLR.". Assemblies do not belong to a specific CLR.
Max Toro
You should quote this, from one of your links: "For example, if both .NET 1.1 and .NET 2.0 shared the same GAC, then a .NET 1.1 application, loading an assembly from this shared GAC, could get .NET 2.0 assemblies, thereby breaking the .NET 1.1 application."
Max Toro
@Max: Agree, added.
Brian R. Bondy
+9  A: 

It doesn't make a lot of sense, the original GAC was already quite capable of storing different versions of assemblies. And there's little reason to assume a program will ever accidentally reference the wrong assembly, all the .NET 4 assemblies got the [AssemblyVersion] bumped up to 4.0.0.0. The new in-process side-by-side feature should not change this.

My guess: there were already too many .NET projects out there that broke the "never reference anything in the GAC directly" rule. I've seen it done on this site several times.

Only one way to avoid breaking those projects: move the GAC. Back-compat is sacred at Microsoft.

Hans Passant
This is the only answer that tries to explain *why* this is the case. +1
Ed Swangren
@Hans Passant: What do you mean by "never reference anything in the GAC directly" rule ?
Max Toro
@Max: there are two copies of the .NET assemblies on your machine. Those meant to be reference assemblies in c:\windows\microsoft.net and c:\program files\reference assemblies. And the ones used at runtime, the GAC @ c:\windows\assembly. They are not the same, 64-bit would be an example. Microsoft did their best to avoid anybody referencing the GAC ones with a shell extension handler. And the Add Reference dialog. Not 100% effective
Hans Passant
@Hans Passant: A direct reference is something like 'c:\WINDOWS\assembly\GAC_MSIL\System\2.0.0.0__b77a5c561934e089\System.dll', I don't see how adding a new version would break that reference.
Max Toro
@Hans: It has the ability to store different versions of the assembly within the same CLR version... so it does make sense.
Brian R. Bondy
It breaks when CLR4 is actually used, it will happily load the incompatible v2 assembly.
Hans Passant
@Hans Passant: "And there's little reason to assume a program will ever accidentally reference the wrong assembly" I think the key here is `Assembly.LoadWithPartialName`, what happens if we have 2 versions of the assembly on the GAC ?
Max Toro
@Max: .NET assemblies are never referenced with a partial name, they are strong-named.
Hans Passant
@Hans Passant: Like you say, referencing the wrong assembly is not the issue, loading the wrong assembly is. "For example, if both .NET 1.1 and .NET 2.0 shared the same GAC, then a .NET 1.1 application, loading an assembly from this shared GAC, could get .NET 2.0 assemblies, thereby breaking the .NET 1.1 application." http://msdn.microsoft.com/en-us/magazine/dd727509.aspx
Max Toro
@Max: It's the exact same argument, without an explanation of how that could happen. "Leaking" doesn't mean anything to me. This starts to smell like "better be safe then sorry".
Hans Passant
A: 

Sorry, I'm afraid I'm going to add more confusion to this thing instead of providing an answer, but it's that all this seems rather messy to me.

If I run a HelloWorld compiled for .Net 4 and check it with ProcessExplorer I can see it's loading mscorlib.ni.dll version 4, which is OK, but that native image is stored in: C:\WINDOWS\assembly\NativeImages_v4.0.30319_32....

instead of C:\WINDOWS\Microsoft.NET\assembly\NativeImages_v4.0.30319_32....

that given that now we have 2 GACs I guess would be the logical storage place...

Also, the Shell extension for the CLR 4 GAC does not seem to work (you can browse the folders normally from explorer), more than a problem I think it's an oddity

As some people said before, I thought the whole sense of the GAC was to avoid dll hell, so having to resort to several GACs seems rather poor...

Xose Lluis
@Xose Lluis: There's one "dll hell" issue in .NET called `LoadWithPartialName`, I bet that's the main reason we have more than one GAC.
Max Toro
+5  A: 

I also wanted to know why 2 GAC and found the following explanation by Mark Miller in the comments section of .NET 4.0 has 2 Global Assembly Cache (GAC):

Mark Miller said... June 28, 2010 12:13 PM

Thanks for the post. "Interference issues" was intentionally vague. At the time of writing, the issues were still being investigated, but it was clear there were several broken scenarios.

For instance, some applications use Assemby.LoadWithPartialName to load the highest version of an assembly. If the highest version was compiled with v4, then a v2 (3.0 or 3.5) app could not load it, and the app would crash, even if there were a version that would have worked. Originally, we partitioned the GAC under it's original location, but that caused some problems with windows upgrade scenarios. Both of these involved code that had already shipped, so we moved our (version-partitioned GAC to another place.

This shouldn't have any impact to most applications, and doesn't add any maintenance burden. Both locations should only be accessed or modified using the native GAC APIs, which deal with the partitioning as expected. The places where this does surface are through APIs that expose the paths of the GAC such as GetCachePath, or examining the path of mscorlib loaded into managed code.

It's worth noting that we modified GAC locations when we released v2 as well when we introduced architecture as part of the assembly identity. Those added GAC_MSIL, GAC_32, and GAC_64, although all still under %windir%\assembly. Unfortunately, that wasn't an option for this release.

Hope it helps future readers.

Jasl
Miller's comments on the linked article does provide an insiders view to the topic.
NimsDotNet