Tuesday, 26 November 2013

// // Leave a Comment

Entity Framework upgrade to 6 configuration and nuget magic

This post discusses the update from Entity Framework 5 to 6 and where where must be vigilant in the face of nuget's magic.
TLDR; Nuget doesn't update .config if there is custom config and setting the Copy Local property on the reference didn't change the actualy csproj file.
Nuget package manager is a wonderful thing. It allows you to simply manage all the of the packages in the solution, update them all automatically, include other dependencies and so on. Wonderful! Glad to have it in the workflow. However, this magic can be dangerous too. Relying on nuget and assuming that it can do no wrong means that debugging problems takes longer because you don't start with it.

The EF Upgrade

I assigned a junior member of the team to perform the upgrade of Entity Framework from 5 to 6 on our greenfield application. We're not live anywhere and are code-first so the database can be deleted each time. We manage Entity Framework (like all our favourite external libraries) with nuget. We did our reading and watched the MSDN video and were prepared.

The upgrade was simple enough, with some namespace changing and when the unit and integration tests went green, the update was committed through TFS gated checkin and all was good.

Missing config blows up integration tests

We use a local Team Foundation Server instance to manage our integration builds. The cloud TFS server orchestrates the integration build and the integration tests are run against a local virtual machine that mimics a live environment as closely as we dare (or can afford!). However, the test kept going bang. The TFS cloud window won't show you the stack trace of a local build (yet) but you can retrieve it through Visual Studio 2013. The message I got was quite clear:

System.InvalidOperationException: No Entity Framework provider found for the ADO.NET provider with invariant name 'System.Data.SqlClient'. Make sure the provider is registered in the 'entityFramework' section of the application config file. See http://go.microsoft.com/fwlink/?LinkId=260882 for more information.

Nice error message, so I followed the instructions and put the configuration in. Nuget usually sets up the configuration for you but it could not in this case because we already had some existing configuration. Nuget does not mess with existing configuration. The error went away but was replaced by a new error.

DLL not copied blows up integration tests

The integration tests were still not happy. This was the error:

System.InvalidOperationException: The Entity Framework provider type 'System.Data.Entity.SqlServer.SqlProviderServices, EntityFramework.SqlServer' registered in the application config file for the ADO.NET provider with invariant name 'System.Data.SqlClient' could not be loaded. Make sure that the assembly-qualified name is used and that the assembly is available to the running application. See http://go.microsoft.com/fwlink/?LinkId=260882 for more information.

When a type cannot be loaded for a DLL that is referenced in a project, it usually means that it has not been copied to the output bin/ directory. When you're not using a type from a referenced library, it will not be copied. That's expected behaviour, when you're linking DLLs dynamically (through reflection), you need to tell .NET that the DLL must be copied.

To ensure a DLL is copied to the output folder, go to the properties of a reference, you will see a property Copy Local. This must be set to true. False means that it will only be copied if there are types being used, which is usually what you want - there's no point copying DLLs across if the code inside isn't being used. More info on it default value here.

For me, that meant that EntityFramework.SqlServer wasn't being copied.

Nuget sets Copy Local property to true but .csproj is not updated

I checked the properties of the EntityFramework.SqlServer reference and it was set to true. Being suspicious, I also checked the .csproj file in a text editor and found that the "Private" property (which represents Copy Local) was not set at all. Changing the property in the Visual Studio 2013 properties window created the tag in the .csproj.

Copy Local was not enough

A quirk that I have not got to the bottom of yet is that Copy Local is not enough to ensure that Entity Framework 6 can find the Sql Provider. The final step was to reference a type in the base domain context (which overrides the databasecontext you get from EF). This is echoed elsewhere, on this Stack Overflow answer. I hate it, it's crazy but I'm out of time and this does work.

Things to learn

The integration tests blowing up on the server means the way in which we develop code has gone awry. The learning point for us here is that nuget is great but don't ignore it when you're investigating issues. If something feels like the install or upgrade went awry then check there first, don't assume it's all gone OK.