Framework Compatibility Going Downhill

Published on: Author: Michael

UPDATE: My post triggered an e-mail exchange with Rowan Miller from the Entity Framework group.  He wanted to explain and clarify some of my comments around EF.  I’ve added them in the appropriate sections below.  Thanks for the clarifications Rowan!


I understand the need for making some breaking changes to the framework as we transition to newer versions but one of the big selling features of .NET has been writing apps without having to worry too much about having the exact same version on end machines.  Microsoft seems to be moving away from this goal with newer releases.  .NET 4.5, as a framework, has a lot of nice, new features.  But as the next version of the framework I think it fails on many levels.  I’m fearful that this is becoming the new trend at Microsoft given the recent releases of .NET and other libraries.


Framework Versioning


While we are on the 7th framework version there have been only 3 CLR versions (currently v4).  Both 4.0 and 4.5 run on the same CLR and are technically the same framework.  This means that if you install 4.5 then all your 4.0 apps are now using the same 4.5 framework even though the code started with 4.0.  4.5 will overwrite the 4.0 binaries because they do not run side-by-side.  This is in contrast to the frameworks based upon CLR 2 (2.0, 3.0 and 3.5).  CLR 2 and CLR 4 can be run side-by-side.  This is really not much different than what we’re used to from native code. 


For this to work well there must be a rule that states that an application written for CLR 4 must continue to work on 4.5 without change.  That is not really the case.  For reasons unknown to anyone but the folks at Microsoft there were changes made to the core libraries that break existing 4.0 applications.  The most blatant is applications based upon Reflection (ie. JustDebug or Reflector).  These applications now throw exceptions because of breaking changes made to the CLR 4 in what should have been an update to the CLR.  Normally you would just not update your application until you’ve had a chance to fix the changes that were made but because 4.5 is the same CLR as 4.0 you can’t.  4.0 apps are just broken and you can’t do anything about it except fix the code.  This clearly violates the rule of compatibility.


What MS should have done is held off on making these changes until the next CLR.  If the changes couldn’t wait then 4.5 should have been 5.0 and we would have gotten a new CLR.  Then CLR 4 apps would continue to work.  Why did MS decided to break the code now?  The answers I keep seeing always come back to WinRT compatibility.  In order to force everybody to use Win8 and Metro MS is going full bore with WinRT.  WinRT is not the full framework (it reminds me a lot of the Compact Framework) so changes to the core library were necessary.  These changes should have been made in the next CLR version so existing apps continue to work but they weren’t.  The result is some apps just won’t work correctly once a user installs the 4.5 framework so expect a lot of tech support calls if your app falls into this category.


Namespaces


Another area where compatibility fails is with moving types.  There are several types that were moved from one assembly and namespace to another (ie. some data annotations from EF).  This is not necessarily a bad thing since it is important to move commonly needed functionality into the framework.  The problem is that it should silently work for existing code and not require changes unless we recompile.  But guess what, you have to recompile your code.  We ran across this recently when we upgraded from EF 4 to EF 5 and some of our core libraries weren’t ready.  I thought one of the attributes added back in 2.0, ForwardTypeToAssembly or something like that, was designed to solve this issue.  Guess not.  The framework should be able to forward the request for a type from one assembly and namespace to another so existing apps continue to work.  This would be a runtime feature and wouldn’t fix compilation errors.  Then a type can be moved and we can recompile when we’re ready to switch frameworks.  Right now you run into problems if you try to use an assembly built against 4.5 with an application using 4.0. 


UPDATE: The data annotations issue comes from us taking some annotation that we’re shipped out-of-band in EF 4.1 (in our NuGet package) and moving them into the .NET Framework. After discussing this with the team that owns the data annotations assembly we decided that having them in the root namespace wasn’t the right thing to do as they were very database specific. That left us with the decision of leaving them out of the framework or taking the namespace change. Regarding compatibility, we were very careful that already compiled code will continue to run, the only time you need to address the namespace change is if you upgrade to EF5.


Assembly version X (or version Y, depending)


Yet another area where MS is making versioning worse is with Entity Framework 5.  When you add a reference to EF 5 you are actually using either EF 5 or 4.x depending upon what framework you are targeting.  Let’s think through this for a second.  You install EF 5 but you look at the assembly references and it is 4.x.  You figure something is wrong so you google and find it is by design.  At some later point you upgrade your application to 4.5 and think you’re using EF 5 but you’re still using 4.x.  That is unless you remember to remove your references to EF and then install them again.   What in the world is going on in Redmond?  Who thought this made any sense and would solve any problems?  I heard that the reason it was done this way was to avoid having to generate 2 NuGet packages.  What??  You just made it harder to know what version of a library someone is using just to avoid creating a NuGet package once!!!  Seriously how hard is it to create 2 different NuGet packages.  I do it all the time.  If EF 5 doesn’t support .NET 4.0 then don’t advertise that it does and swap assemblies behind the scenes. You’ve just made it harder on everybody going forward.


UPDATE: Regarding the 4.x vs. 5.x version number. We have to have separate assemblies because there are different features that light up on .NET 4.5. For example, you can’t use the enum support in EF5 when using .NET 4.0. Both assemblies are EF5, but we decided to have different assembly versions so that the CLR will treat them as different – important because they have different API surface in them. The assembly informational version, file version etc. are all 5. (Response: This is different than what I’ve been hearing about the differences between 4.0 and 4.5 versions of EF.  It does appear they are more different than the same which pushes me further down the path that they shouldn’t have been released this way.  It seems like it’ll introduce compatibility issues when making unrelated EF changes – aka framework upgrades).


Future


I can’t say what the future holds for the framework but if it continues down the path it is going now I foresee we’ll be accusing it of versioning nightmares just like we had back in the DLL days.  The rules of versioning isn’t really that hard so I fail to see why the teams at MS are struggling with it.  In case you need a refresher here are the versioning rules:


  • A minor update (ie 4.0 and 4.5) guarantees that an application behaves the same whether the update is installed or not (although bug fixes and any additions added in the update will not exist). 
  • An application can explicitly target the update version if needed.
  • A breaking change that requires rebuilding an application is always a major update (ie 2.0 to 4.0).
  • An application built against a major version will continue to work unchanged against an updated version.
  • Installing version X of a product ensures that version X is being used.  If version X is not supported on the targeted platform/version then it won’t install.
  • For libraries that have breaking changes multiple versions will be shipped to support the various versions the library needs to support.

I think at this point Microsoft should just stop the minor update process, except for pure bug fixes, and stick with major version changes only.  They are guaranteed to make at least one breaking change in the minor updates so they are violating the rules.  People are already starting to grumble about it.  How loud does the complaints have to get before Microsoft introduces the “next” solution to DLL hell?  I think the solution already exists.  It is called proper versioning of the framework.