Raspberry Pi + Mono + EF6 – defeat!

After (or maybe even before) I successfully made EF6 work on Mono I thought it would be cool to try it on Raspberry Pi. Since my project already worked on Mono I thought it should be relatively easy to do. The first thing I had to do was to install Mono on my Raspberry Pi. It is easy and requires running just two commands:

sudo apt-get update
sudo apt-get install mono-runtime

Since my project uses MySQL I also had to install MySQL database. Again it was as easy as executing:

sudo apt-get install mysql-server

After that I used mysql and mysqladmin tools to create a user and a database that would be used by my app. With my Raspberry Pi ready I copied my little app to the Raspberry Pi and ran it using the mono command:

mono EF6MySqlTest.exe

only to see it failing with the following exception:

Missing method .ctor in assembly /home/pi/monotest/MONOTest/EntityFramework.dll, type
System.Runtime.CompilerServices.ExtensionAttribute
Can't find custom attr constructor image: 
 /home/pi/monotest/MONOTest/EntityFramework.dll mtoken: 0x0a00006a

Unhandled Exception: System.TypeLoadException: Could not load
type 'System.Runtime.CompilerServices.ExtensionAttribute' from assembly 'EntityFramework'.
  at EF6MySqlTest.SimpleContext..ctor () [0x00000] in :0
  at EF6MySqlTest.Program.Main (System.String[] args) [0x00000] in :0
[ERROR] FATAL UNHANDLED EXCEPTION: System.TypeLoadException: Could not load
type 'System.Runtime.CompilerServices.ExtensionAttribute' from assembly 'EntityFramework'.
  at EF6MySqlTest.SimpleContext..ctor () [0x00000] in :0
  at EF6MySqlTest.Program.Main (System.String[] args) [0x00000] in :0

The exception startled me a little bit. The very first thing I noticed was that the app failed even before it started executing. I looked at the documentation for the ExtensionAttribute type on the MSDN but I did not find anything extraordinary. Only comparing the documentation for .NET Framework 4.5 with the documentation for .NET Framework 4 gave me the clue. In the .NET Framework 4.5 the ExtensionAttribute type lives in the mscorlib.dll while in the .NET Framework 4 it lived in the System.Core.dll assembly. In general it is OK. CLR has this feature where a type can be moved from one assembly to another as long as its full name does not change and the original assembly contains the TypeForwardedToAttribute assembly level attribute pointing to the assembly the type lives in the new version (this is called type forwarding and you can find more details here). The exception I got just said that the EntityFramework assembly depends on the ExtensionAttribute which could not be found. Putting the information together I inferred what the exception did not say – namely that the ExtensionAttribute type could not be found in the mscorlib.dll assembly. This was just one step away from solving my problem. If the mscorlib.dll did not contain the ExtensionAttribute type then the type had to be in the System.Core.dll assembly which in turn meant that the version of the .NET Framework supported by Mono packages I installed was 4 and my application was compiled targeting version 4.5. This was not a big deal. Entity Framework 6 supports both .NET Framework 4.5 and .NET Framework 4 so I went back and recompiled my app so that it targets .NET Framework 4, copied it to my Raspberry Pi started again and… failed again. This time the exception looked like this:

Unhandled Exception: 
System.TypeInitializationException: An exception was thrown by the type initializer for System.Data.Entity.Config.DbConfigurationManager 
---> System.TypeInitializationException: An exception was thrown by the type initializer for System.Data.SqlTypes.SqlDecimal 
---> System.OverflowException: Can't convert to SqlDecimal, Out of range
  at System.Data.SqlTypes.SqlDecimal..ctor (Byte bPrecision, Byte bScale, Boolean fPositive, Int32 data1, Int32 data2, Int32 data3, Int32 data4) [0x00000] in :0
  at System.Data.SqlTypes.SqlDecimal..cctor () [0x00000] in :0
  --- End of inner exception stack trace ---
  at System.Data.DataColumn..ctor (System.String columnName, System.Type dataType, System.String expr, MappingType type) [0x00000] in :0
  at System.Data.DataColumn..ctor (System.String columnName, System.Type dataType) [0x00000] in :0
  at System.Data.Common.DbProviderFactoriesConfigurationHandler.CreateDataSet () [0x00000] in :0

  [... omitted for brevity ...]

  --- End of inner exception stack trace ---
  at System.Data.Entity.DbContext.InitializeLazyInternalContext (IInternalConnection internalConnection, System.Data.Entity.Infrastructure.DbCompiledModel model) [0x00000] in :0
  at System.Data.Entity.DbContext..ctor (System.String nameOrConnectionString) [0x00000] in :0
  at EF6MySqlTest.SimpleContext..ctor () [0x00000] in :0
  at EF6MySqlTest.Program.Main (System.String[] args) [0x00000] in :0

The good thing was that the previous exception was gone and that I could see some familiar Entity Framework methods in the stack trace. This means that this time the application at least tried running. But then I got to the System.OverflowException: Can't convert to SqlDecimal, Out of range exception thrown from System.Data.SqlTypes.SqlDecimal..ctor and started feeling a bit uneasy. This did not look like a bug in my code. On the contrary, it looked like some problem in Mono which I most likely would not be able to fix or work around. To confirm that it was unrelated to my app or EF6 I played a bit and came up with the following program that duplicated the issue (yes, it is just one line of code and it probably could be simplified even more):

public static void Main()
{
  Console.WriteLine(DbProviderFactories.GetFactoryClasses().Rows.Count);
}

Running the above code resulted in the same exception I saw when running my app:

Unhandled Exception: System.TypeInitializationException: 
An exception was thrown by the type initializer for System.Data.SqlTypes.SqlDecimal —>
System.OverflowException: Can’t convert to SqlDecimal, Out of range
 at System.Data.SqlTypes.SqlDecimal..ctor (Byte bPrecision, Byte bScale, Boolean fPositive, Int32 data1, Int32 data2, Int32 data3, Int32 data4) [0x00000] in :0
 at System.Data.SqlTypes.SqlDecimal..cctor () [0x00000] in :0

I looked around a little bit and found two bugs (bug report 1 and bug report 2) that looked similar to what I was seeing. One of them even contained a link to a patch with the fix which made me think that this might be fixed in a newer version of Mono. Soon I found this page which mentions a Debian Experimental version which is 3.0.6 as opposed to 2.10.8 installed on the Raspberry Pi. I tried to follow the steps listed on the page but unfortunately I could not install the experimental version due to missing package dependencies:

Reading package lists... Done
Building dependency tree
Reading state information... Done
Some packages could not be installed. This may mean that you have
requested an impossible situation or if you are using the unstable
distribution that some required packages have not yet been created
or been moved out of Incoming.
The following information may help to resolve the situation:

The following packages have unmet dependencies:
mono-complete : Depends: mono-devel (= 2.10.8.1-5) but it is not going to be installed
Depends: mono-mcs (= 2.10.8.1-5) but it is not going to be installed
Depends: mono-gmcs (= 2.10.8.1-5) but it is not going to be installed
Depends: mono-dmcs (= 2.10.8.1-5) but it is not going to be installed
Depends: mono-csharp-shell (= 2.10.8.1-5) but it is not going to be installed
Depends: mono-2.0-gac (= 2.10.8.1-5) but it is not going to be installed
Depends: mono-2.0-service (= 2.10.8.1-5) but it is not going to be installed
Depends: mono-4.0-service (= 2.10.8.1-5) but it is not going to be installed
Depends: monodoc-base (= 2.10.8.1-5) but it is not going to be installed
Depends: monodoc-manual (= 2.10.8.1-5) but it is not going to be installed
Depends: libmono-cil-dev (= 2.10.8.1-5) but it is not going to be installed
E: Unable to correct problems, you have held broken packages.

I am not a Linux guru but really wanted to make it work (or at least understand why it did not want to work) so I asked a question on stackoverflow and wrote an email to Mirco Bauer who seems to overlook Mono 3.0 on Debian. Unfortunately I have not received any answers which would help me resolve the problem so I gave up and forgot about this when suddenly after a couple weeks I got a response from Mirco. He wrote:

Mono has no working armhf port, but there is now work done to create an official armhf port. The only option for Mono is armel right now.

I have to admit that when I read the message I did not really understand it. So I did some research and found this post. It enlightened me and made me realize that the bug report I looked at before and the Raspberry Pi downloads page already had the answer I needed. I should have just tried understanding some details included there instead of ignoring them :). To put it simply
– the Raspbian “wheezy” is an armhf distro (and – as per email from Mirco – “Mono has no working armhf port”) while the Soft-float Debian “wheezy” is an armel image. The difference between the images is how they handle floating point operations. This gave me some hope. I downloaded the Soft-float Debian “wheezy” image and quickly prep my Raspberry Pi by installing mono and MySQL. One of the very first thing I did was to run the repro app to verify if the OverflowException is gone. Indeed this time instead of the exception the app wrote 7 (I wish it had been 42 but 7 is still better than OverflowException). This started looking promising so I tried to run the “proper” app. I got a few uninteresting exceptions (like no permissions to access registry etc.) but then I got this:

Unhandled Exception: System.TypeInitializationException: An exception was thrown by the type initializer for System.Data.Entity.ModelConfiguration.Conventions.Sets.V2ConventionSet ---> System.TypeInitializationException: An exception was thrown by the type initializer for System.Data.Entity.ModelConfiguration.Conventions.Sets.V1ConventionSet ---> System.TypeLoadException: Could not load type 'System.Data.Entity.ModelConfiguration.Conventions.KeyAttributeConvention' from assembly 'EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'.
  --- End of inner exception stack trace ---
  at System.Data.Entity.ModelConfiguration.Conventions.Sets.V2ConventionSet..cctor () [0x00000] in <filename unknown>:0
  --- End of inner exception stack trace ---
  at System.Data.Entity.DbModelBuilder.SelectConventionSet (DbModelBuilderVersion modelBuilderVersion) [0x00000] in <filename unknown>:0
  at System.Data.Entity.DbModelBuilder..ctor (System.Data.Entity.ModelConfiguration.Configuration.ModelConfiguration modelConfiguration, DbModelBuilderVersion modelBuilderVersion) [0x00000] in <filename unknown>:0
  at System.Data.Entity.DbModelBuilder..ctor (DbModelBuilderVersion modelBuilderVersion) [0x00000] in <filename unknown>:0
  at System.Data.Entity.Internal.LazyInternalContext.CreateModelBuilder () [0x00000] in <filename unknown>:0
  at System.Data.Entity.Internal.LazyInternalContext.CreateModel (System.Data.Entity.Internal.LazyInternalContext internalContext) [0x00000] in <filename unknown>:0
  at System.Data.Entity.Internal.RetryLazy`2[System.Data.Entity.Internal.LazyInternalContext,System.Data.Entity.Infrastructure.DbCompiledModel].GetValue (System.Data.Entity.Internal.LazyInternalContext input) [0x00000] in <filename unknown>:0
[ERROR] FATAL UNHANDLED EXCEPTION: System.TypeInitializationException: An exception was thrown by the type initializer for System.Data.Entity.ModelConfiguration.Conventions.Sets.V2ConventionSet ---> System.TypeInitializationException: An exception was thrown by the type initializer for System.Data.Entity.ModelConfiguration.Conventions.Sets.V1ConventionSet ---> System.TypeLoadException: Could not load type 'System.Data.Entity.ModelConfiguration.Conventions.KeyAttributeConvention' from assembly 'EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'.
  --- End of inner exception stack trace ---
  at System.Data.Entity.ModelConfiguration.Conventions.Sets.V2ConventionSet..cctor () [0x00000] in <filename unknown>:0
  --- End of inner exception stack trace ---
  at System.Data.Entity.DbModelBuilder.SelectConventionSet (DbModelBuilderVersion modelBuilderVersion) [0x00000] in <filename unknown>:0
  at System.Data.Entity.DbModelBuilder..ctor (System.Data.Entity.ModelConfiguration.Configuration.ModelConfiguration modelConfiguration, DbModelBuilderVersion modelBuilderVersion) [0x00000] in <filename unknown>:0
  at System.Data.Entity.DbModelBuilder..ctor (DbModelBuilderVersion modelBuilderVersion) [0x00000] in <filename unknown>:0
  at System.Data.Entity.Internal.LazyInternalContext.CreateModelBuilder () [0x00000] in <filename unknown>:0
  at System.Data.Entity.Internal.LazyInternalContext.CreateModel (System.Data.Entity.Internal.LazyInternalContext internalContext) [0x00000] in <filename unknown>:0
  at System.Data.Entity.Internal.RetryLazy`2[System.Data.Entity.Internal.LazyInternalContext,System.Data.Entity.Infrastructure.DbCompiledModel].GetValue (System.Data.Entity.Internal.LazyInternalContext input) [0x00000] in <filename unknown>:0

The most important thing here is Could not load type 'System.Data.Entity.ModelConfiguration.Conventions.KeyAttributeConvention' from assembly 'EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'.. I looked at the KeyAttributeConvention and the class is very simple. There is actually hardly anything that could break here. Since the only dependency this class has is the KeyAttribute type it had to be the thing that caused problems. Again, I tried to isolate the issue so I added the following line to my test app:

Console.WriteLine(typeof(System.ComponentModel.DataAnnotations.KeyAttribute).Name);

After I did this the test app failed to compile complaining that it cannot resolve the KeyAttribute type and recommended adding references to an assembly containing a definition of this type even though I did provide a reference to System.ComponentModel.DataAnnotations.dll. This was weird. I decided to list all the types from the System.ComponentModel.DataAnnotations.dll assembly to make sure that the KeyAttribute is there so I replaced the line referring to the KeyAttribute with:

foreach(var t in typeof(ScaffoldColumnAttribute).Assembly.GetTypes())
{
    Console.WriteLine(t.FullName);
}

After running the app I looked at the type names and could not find the KeyAttribute type. This is unfortunate since it means that it’s impossible to run EF6 on the stable version (2.10.8) of Mono I installed. I thought the attribute might have been added in a newer version so I tried installing the experimental version. Unfortunately this did not really work. This time I did not get any errors but I got the message mono-complete is already the newest version and nothing was installed.
This was an interesting journey and I am a bit disappointed I was not able to achieve what I wanted especially because the goal seemed so close. The bright side is that I learnt quite a lot of interesting things and I believe it will be possible to run EF6 on the Raspberry Pi when either Mono works on armhf or there is a newer version of Mono for armel available. I will give it a try for sure in the future!

Entity Framework 6 on Mono

I had been wanting to try Entity Framework 6 with Mono for a long time and finally after a check-in I made on Friday I was able to spare some time to do this. I decided to go with MySQL and dotConnect for MySQL from Devart. After skimming the tutorial on using Devart’s providers with EF6 I opened Visual Studio 2012, created a new project, installed the EF6 alpha3 package from NuGet and thought I was almost done. The only problem was that the dead simple (one entity) EF6 based app I created did not work. I did not expect the exception I got and quickly realized why the tutorial I was following started with “Entity Framework 6 Alpha 2 support is implemented…”. Things got a little better when I went back from EF6 Alpha 3 to EF6 Alpha 2 but still did not work. I found that I was missing one of the dlls and then that I used wrong revision number in the version of the provider assemblies (interestingly the version of the assembly containing the provider factory was 7.6.217.0 while the version of the assembly containing provider services was 7.6.217.6 – I expected both assemblies to have the same version). The last time I used MySQL was… some time ago (OK, it was in the 20th century) and it was the first time I used it with Code First (kinda obvious). Even after setting all the versions correctly the app still did not want to run. I was able to get the edmx file with the EdmxWriter (which is a good sign meaning that the provider is configured correctly and working) but then the app would die because it could not create the database. After a quick search I found that I need to create an empty database using MySQL command prompt and then EF should be able to add tables to this database. Indeed – after I created the empty database that app did not throw and I was able to add entities to the database and then read them. With this I was ready to move to the Mono world. So, I installed the latest version of Mono and started playing a little bit with it. mcs.exe (the compiler) worked just fine. However since I could build my app with Visual Studio and ran it using mono.exe I decided not to use the mcs.exe. Unfortunately the app would not quite work. It would start and then throw System.Configuration.ConfigurationErrorsException: Failed to find or load the registered .Net Framework Data Provider .... Which basically means that the ADO.NET/EF provider I used was not registered. I think the reason was that I installed Mono after I installed the provider and therefore the entry in the machine.config was missing. I added the following entry:

  <system.data>
    <DbProviderFactories>
      <remove invariant="Devart.Data.MySql" />
      <add name="dotConnect for MySQL" invariant="Devart.Data.MySql" 
           description="Devart dotConnect for MySQL" 
           type="Devart.Data.MySql.MySqlProviderFactory, Devart.Data.MySql, Version=7.6.217.0, Culture=neutral, PublicKeyToken=09af7300eec23701" />
    </DbProviderFactories>
  </system.data>

to my app.config file and I was able to get a little bit further. (Just in case – you can just add the “add” line from the above to the Mono’s machine.config in the provider section and it should work as well). Now when I ran my app I got this:

Unhandled Exception:
System.NullReferenceException: Object reference not set to an instance of an object
  at System.Data.Entity.Core.EntityModel.SchemaObjectModel.EntityKeyElement.Validate () [0x00000] in :0
  at System.Data.Entity.Core.EntityModel.SchemaObjectModel.SchemaEntityType.Validate () [0x00000] in :0
  at System.Data.Entity.Core.EntityModel.SchemaObjectModel.Schema.Validate () [0x00000] in :0
  at System.Data.Entity.Core.EntityModel.SchemaObjectModel.Schema.ValidateSchema () [0x00000] in :0
  at System.Data.Entity.Core.EntityModel.SchemaObjectModel.SchemaManager.ParseAndValidate (IEnumerable`1 xmlReaders, IEnumerable`1 sourceFilePaths, SchemaDataModelOption dataModel, System.Data.Entity.Core.EntityModel.SchemaObjectModel.AttributeValueNotification providerNotification, System.Data.Entity.Core.EntityModel.SchemaObjectModel.AttributeValueNotification providerManifestTokenNotification, System.Data.Entity.Core.EntityModel.SchemaObjectModel.ProviderManifestNeeded providerManifestNeeded, IList`1&amp; schemaCollection) [0x00000] in :0
  at System.Data.Entity.Core.Metadata.Edm.StoreItemCollection+Loader.LoadItems (IEnumerable`1 xmlReaders, IEnumerable`1sourceFilePaths) [0x00000] in :0
  at System.Data.Entity.Core.Metadata.Edm.StoreItemCollection+Loader..ctor (IEnumerable`1 xmlReaders, IEnumerable`1 sourceFilePaths, Boolean throwOnError) [0x00000] in :0
  at System.Data.Entity.Core.Metadata.Edm.StoreItemCollection.Init (IEnumerable`1 xmlReaders, IEnumerable`1 filePaths, Boolean throwOnError, System.Data.Entity.Core.Common.DbProviderManifest&amp; providerManifest, System.Data.Common.DbProviderFactory&amp; providerFactory, System.String&amp; providerManifestToken, System.Data.Entity.Core.Common.Utils.Memoizer`2&amp; cachedCTypeFunction) [0x00000] in :0
  at System.Data.Entity.Core.Metadata.Edm.StoreItemCollection..ctor (IEnumerable`1 xmlReaders) [0x00000] in :0
  at System.Data.Entity.Utilities.XDocumentExtensions.GetStoreItemCollection (System.Xml.Linq.XDocument model, System.Data.Entity.Infrastructure.DbProviderInfo&amp; providerInfo) [0x00000] in :0
  at System.Data.Entity.Migrations.Infrastructure.EdmModelDiffer.Diff (System.Xml.Linq.XDocument sourceModel, System.Xml.Linq.XDocument targetModel, Nullable`1 includeSystemOperations) [0x00000] in :0
  at System.Data.Entity.Internal.InternalContext.ModelMatches (System.Xml.Linq.XDocument model) [0x00000] in :0
  at System.Data.Entity.Internal.ModelCompatibilityChecker.CompatibleWithModel (System.Data.Entity.Internal.InternalContext internalContext, System.Data.Entity.Internal.ModelHashCalculator modelHashCalculator, Boolean throwIfNoMetadata) [0x00000] in :0
  at System.Data.Entity.Internal.InternalContext.CompatibleWithModel (Boolean throwIfNoMetadata) [0x00000] in :0
  at System.Data.Entity.Database.CompatibleWithModel (Boolean throwIfNoMetadata) [0x00000] in :0
  at System.Data.Entity.CreateDatabaseIfNotExists`1[System.Data.Entity.DbContext].InitializeDatabase (System.Data.Entity.DbContext context) [0x00000] in :0
  at System.Data.Entity.Internal.InternalContext+c__DisplayClassc`1[EF6MySqlTest.SimpleContext].b__b () [0x00000] in :0
  at System.Data.Entity.Internal.InternalContext.PerformInitializationAction (System.Action action) [0x00000] in :0
  at System.Data.Entity.Internal.InternalContext.PerformDatabaseInitialization () [0x00000] in :0
  at System.Data.Entity.Internal.LazyInternalContext.b__4 (System.Data.Entity.Internal.InternalContext c) [0x00000] in :0
  at System.Data.Entity.Internal.RetryAction`1[System.Data.Entity.Internal.InternalContext].PerformAction (System.Data.Entity.Internal.InternalContext input) [0x00000] in :0
[ERROR] FATAL UNHANDLED EXCEPTION: System.NullReferenceException: Object reference not set to an instance of an object
  at System.Data.Entity.Core.EntityModel.SchemaObjectModel.EntityKeyElement.Validate () [0x00000] in :0
  at System.Data.Entity.Core.EntityModel.SchemaObjectModel.SchemaEntityType.Validate () [0x00000] in :0
  at System.Data.Entity.Core.EntityModel.SchemaObjectModel.Schema.Validate () [0x00000] in :0
  at System.Data.Entity.Core.EntityModel.SchemaObjectModel.Schema.ValidateSchema () [0x00000] in :0
  at System.Data.Entity.Core.EntityModel.SchemaObjectModel.SchemaManager.ParseAndValidate (IEnumerable`1 xmlReaders, IEnumerable`1 sourceFilePaths, SchemaDataModelOption dataModel, System.Data.Entity.Core.EntityModel.SchemaObjectModel.AttributeValueNotification providerNotification, System.Data.Entity.Core.EntityModel.SchemaObjectModel.AttributeValueNotification providerManifestTokenNotification, System.Data.Entity.Core.EntityModel.SchemaObjectModel.ProviderManifestNeeded providerManifestNeeded, IList`1&amp; schemaCollection) [0x00000] in :0
  at System.Data.Entity.Core.Metadata.Edm.StoreItemCollection+Loader.LoadItems (IEnumerable`1 xmlReaders, IEnumerable`1sourceFilePaths) [0x00000] in :0
  at System.Data.Entity.Core.Metadata.Edm.StoreItemCollection+Loader..ctor (IEnumerable`1 xmlReaders, IEnumerable`1 sourceFilePaths, Boolean throwOnError) [0x00000] in :0
  at System.Data.Entity.Core.Metadata.Edm.StoreItemCollection.Init (IEnumerable`1 xmlReaders, IEnumerable`1 filePaths, Boolean throwOnError, System.Data.Entity.Core.Common.DbProviderManifest&amp; providerManifest, System.Data.Common.DbProviderFactory&amp; providerFactory, System.String&amp; providerManifestToken, System.Data.Entity.Core.Common.Utils.Memoizer`2&amp; cachedCTypeFunction) [0x00000] in :0
  at System.Data.Entity.Core.Metadata.Edm.StoreItemCollection..ctor (IEnumerable`1 xmlReaders) [0x00000] in :0
  at System.Data.Entity.Utilities.XDocumentExtensions.GetStoreItemCollection (System.Xml.Linq.XDocument model, System.Data.Entity.Infrastructure.DbProviderInfo&amp; providerInfo) [0x00000] in :0
  at System.Data.Entity.Migrations.Infrastructure.EdmModelDiffer.Diff (System.Xml.Linq.XDocument sourceModel, System.Xml.Linq.XDocument targetModel, Nullable`1 includeSystemOperations) [0x00000] in :0
  at System.Data.Entity.Internal.InternalContext.ModelMatches (System.Xml.Linq.XDocument model) [0x00000] in :0
  at System.Data.Entity.Internal.ModelCompatibilityChecker.CompatibleWithModel (System.Data.Entity.Internal.InternalContext internalContext, System.Data.Entity.Internal.ModelHashCalculator modelHashCalculator, Boolean throwIfNoMetadata) [0x00000] in :0
  at System.Data.Entity.Internal.InternalContext.CompatibleWithModel (Boolean throwIfNoMetadata) [0x00000] in :0
  at System.Data.Entity.Database.CompatibleWithModel (Boolean throwIfNoMetadata) [0x00000] in :0
  at System.Data.Entity.CreateDatabaseIfNotExists`1[System.Data.Entity.DbContext].InitializeDatabase (System.Data.Entity.DbContext context) [0x00000] in :0
  at System.Data.Entity.Internal.InternalContext+c__DisplayClassc`1[EF6MySqlTest.SimpleContext].b__b () [0x00000] in :0
  at System.Data.Entity.Internal.InternalContext.PerformInitializationAction (System.Action action) [0x00000] in :0
  at System.Data.Entity.Internal.InternalContext.PerformDatabaseInitialization () [0x00000] in :0
  at System.Data.Entity.Internal.LazyInternalContext.b__4 (System.Data.Entity.Internal.InternalContext c) [0x00000] in :0
  at System.Data.Entity.Internal.RetryAction`1[System.Data.Entity.Internal.InternalContext].PerformAction (System.Data.Entity.Internal.InternalContext input) [0x00000] in :0

WTF? The same app worked just fine on .NET Framework! Obviously there was a bug somewhere but where? Mono? EF? .NET Framework? I followed the advice given by John Robins from Wintellect (http://www.wintellect.com/cs/blogs/jrobbins/default.aspx) during the “Debugging Windows Applications” training I took (too) many years ago and prayed that the bug was in the code base I own (because “in this case you can fix it”). I looked at some tutorials on debugging Mono apps but quickly felt it was too much to learn. Then I installed WinDbg but without symbols I could see only this:

ChildEBP RetAddr  Args to Child              
WARNING: Stack unwind information not available. Following frames may be wrong.
0022f5ec 76ccbbf7 00000000 77e8f3b0 ffffffff ntdll!KiFastSystemCallRet
*** ERROR: Symbol file could not be found.  Defaulted to export symbols for C:\Windows\system32\msvcrt.dll - 
0022f600 758a36dc 00000001 0022f644 758a3372 kernel32!ExitProcess+0x15
0022f60c 758a3372 00000001 172bd79c 016b6698 msvcrt!exit+0x32
0022f644 758a36bb 00000001 00000000 00000000 msvcrt!dup+0x2a9
*** ERROR: Symbol file could not be found.  Defaulted to export symbols for C:\Program Files\Mono-3.0.9\bin\mono-2.0.dll - 
0022f658 65a62b3a 00000001 65bba06c 0546e928 msvcrt!exit+0x11
0022f678 65a61fe6 05af88c0 016b6698 00000000 mono_2_0!mono_invoke_unhandled_exception_hook+0x6a
0022f7e8 65aa7aba 00000000 00000000 00000000 mono_2_0!mono_print_thread_dump_from_ctx+0xaa6
0022f898 01550e8f 0022f8b0 05af88c0 03cd75ac mono_2_0!mono_x86_throw_exception+0xca
0022f968 03cd6a64 03d6f230 01c53cc0 03ee0d58 0x1550e8f
0022f9a8 03cd6968 01c53cc0 03ee0d90 0022f9bc 0x3cd6a64
0022f9c8 01bc19f5 01c53cc0 02b8e090 0022f9dc 0x3cd6968
0022f9e8 01bc1984 01c53cc0 02b8e090 0022f9fc 0x1bc19f5
0022fa08 01bc1935 01c53cc0 00538df0 0022fa1c 0x1bc1984
0022fa28 01bc18c5 01c593c0 02b8e090 0022fa3c 0x1bc1935
0022fa48 01bc17be 01c593c0 016bd7a8 0022fa5c 0x1bc18c5
0022fa68 01bc1740 01c593c0 02bc38f8 00000004 0x1bc17be
0022faa8 01bc1654 01c593c0 02b8e060 0022fabc 0x1bc1740
0022fba8 65b5340a 002c73d8 00000001 0022fbf8 0x1bc1654
0022fbb8 659cc21c 00000000 0022fc38 00000000 mono_2_0!mono_runtime_class_init+0x1a
0022fbf8 65b52e42 002c81b0 00000000 0022fc38 mono_2_0!mono_jit_compile_method+0x10c
0022fc18 65b55682 002c81b0 00000000 0022fc38 mono_2_0!mono_runtime_invoke+0x42
0022fc48 65a386b2 002c81b0 00539e00 00000000 mono_2_0!mono_runtime_exec_main+0xd2
*** ERROR: Module load completed but symbols could not be loaded for image00400000
0022fec8 004014bd 00000002 002c1710 00000000 mono_2_0!mono_main+0x1562
0022ff28 004010bb 00000002 002c16e8 002c1bb8 image00400000+0x14bd
0022ff68 004012a8 00000001 00000000 00000000 image00400000+0x10bb
0022ff88 76cbed6c 7ffdd000 0022ffd4 7701377b image00400000+0x12a8
0022ff94 7701377b 7ffdd000 7e6b10f1 00000000 kernel32!BaseThreadInitThunk+0x12
0022ffd4 7701374e 00401290 7ffdd000 00000000 ntdll!RtlInitializeExceptionChain+0xef
0022ffec 00000000 00401290 7ffdd000 00000000 ntdll!RtlInitializeExceptionChain+0xc2

Maybe there are people on this planet who can make sense out of it but I am not there yet. Without symbols I could do not much. So, I ended up sync’ing my source to the changeset around the date we shipped (git reset --hard 9a52aa5f) and this time WinDbg showed this:

This should never be null, since if we were not able to resolve, we should have never reached to this point
---- Assert Long Message ----
   at System.Diagnostics.DefaultTraceListener.Fail(System.String message, System.String detailMessage)
   at System.Diagnostics.TraceListener.Fail(System.String message)
   at System.Diagnostics.DefaultTraceListener.Fail(System.String message)
   at System.Diagnostics.TraceImpl.Fail(System.String message)
   at System.Diagnostics.TraceImpl.Assert(Boolean condition, System.String message)
   at System.Diagnostics.Debug.Assert(Boolean condition, System.String message)
   at System.Data.Entity.Core.EntityModel.SchemaObjectModel.EntityKeyElement.Validate()
   at System.Data.Entity.Core.EntityModel.SchemaObjectModel.SchemaEntityType.Validate()
   at System.Data.Entity.Core.EntityModel.SchemaObjectModel.Schema.Validate()
   at System.Data.Entity.Core.EntityModel.SchemaObjectModel.Schema.ValidateSchema()
   at System.Data.Entity.Core.EntityModel.SchemaObjectModel.SchemaManager.ParseAndValidate(IEnumerable`1 xmlReaders, IEnumerable`1 sourceFilePaths, SchemaDataModelOption dataModel, System.Data.Entity.Core.EntityModel.SchemaObjectModel.AttributeValueNotification providerNotification, System.Data.Entity.Core.EntityModel.SchemaObjectModel.AttributeValueNotification providerManifestTokenNotification, System.Data.Entity.Core.EntityModel.SchemaObjectModel.ProviderManifestNeeded providerManifestNeeded, IList`1 ByRef schemaCollection)
   at System.Data.Entity.Core.Metadata.Edm.StoreItemCollection+Loader.LoadItems(IEnumerable`1 xmlReaders, IEnumerable`1 sourceFilePaths)
   at System.Data.Entity.Core.Metadata.Edm.StoreItemCollection+Loader..ctor(IEnumerable`1 xmlReaders, IEnumerable`1 sourceFilePaths, Boolean throwOnError)
   at System.Data.Entity.Core.Metadata.Edm.StoreItemCollection.Init(IEnumerable`1 xmlReaders, IEnumerable`1 filePaths, Boolean throwOnError, System.Data.Entity.Core.Common.DbProviderManifest ByRef providerManifest, System.Data.Common.DbProviderFactory ByRef providerFactory, System.String ByRef providerManifestToken, System.Data.Entity.Core.Common.Utils.Memoizer`2 ByRef cachedCTypeFunction)
   at System.Data.Entity.Core.Metadata.Edm.StoreItemCollection..ctor(IEnumerable`1 xmlReaders)
   at System.Data.Entity.Utilities.XDocumentExtensions.GetStoreItemCollection(System.Xml.Linq.XDocument model, System.Data.Entity.Infrastructure.DbProviderInfo ByRef providerInfo)
   at System.Data.Entity.Migrations.Infrastructure.EdmModelDiffer.Diff(System.Xml.Linq.XDocument sourceModel, System.Xml.Linq.XDocument targetModel, Nullable`1 includeSystemOperations)
   at System.Data.Entity.Internal.InternalContext.ModelMatches(System.Xml.Linq.XDocument model)
   at System.Data.Entity.Internal.ModelCompatibilityChecker.CompatibleWithModel(System.Data.Entity.Internal.InternalContext internalContext, System.Data.Entity.Internal.ModelHashCalculator modelHashCalculator, Boolean throwIfNoMetadata)
   at System.Data.Entity.Internal.InternalContext.CompatibleWithModel(Boolean throwIfNoMetadata)
   at System.Data.Entity.Database.CompatibleWithModel(Boolean throwIfNoMetadata)
   at System.Data.Entity.CreateDatabaseIfNotExists`1[[System.Data.Entity.DbContext, EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]].InitializeDatabase(System.Data.Entity.DbContext context)
   at System.Data.Entity.Internal.InternalContext+c__DisplayClassc`1[[EF6MySqlTest.SimpleContext, EF6MySqlTest, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]].b__b()
   at System.Data.Entity.Internal.InternalContext.PerformInitializationAction(System.Action action)
   at System.Data.Entity.Internal.InternalContext.PerformDatabaseInitialization()
   at System.Data.Entity.Internal.LazyInternalContext.b__4(System.Data.Entity.Internal.InternalContext c)
   at System.Data.Entity.Internal.RetryAction`1[[System.Data.Entity.Internal.InternalContext, EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]].PerformAction(System.Data.Entity.Internal.InternalContext input)
   at System.Data.Entity.Internal.LazyInternalContext.InitializeDatabaseAction(System.Action`1 action)
   at System.Data.Entity.Internal.LazyInternalContext.InitializeDatabase()
   at System.Data.Entity.Internal.InternalContext.Initialize()
   at System.Data.Entity.Internal.InternalContext.GetEntitySetAndBaseTypeForType(System.Type entityType)
   at System.Data.Entity.Internal.Linq.InternalSet`1[[EF6MySqlTest.Customer, EF6MySqlTest, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]].Initialize()
   at System.Data.Entity.Internal.Linq.InternalSet`1[[EF6MySqlTest.Customer, EF6MySqlTest, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]].get_InternalContext()
   at System.Data.Entity.Internal.Linq.InternalSet`1[[EF6MySqlTest.Customer, EF6MySqlTest, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]].ActOnSet(System.Action action, EntityState newState, System.Object entity, System.String methodName)
   at System.Data.Entity.Internal.Linq.InternalSet`1[[EF6MySqlTest.Customer, EF6MySqlTest, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]].Add(System.Object entity)
   at System.Data.Entity.DbSet`1[[EF6MySqlTest.Customer, EF6MySqlTest, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]].Add(EF6MySqlTest.Customer entity)
   at EF6MySqlTest.Program.Main(System.String[] args)

Apparently my prayer was heard and this line proves it: at System.Diagnostics.Debug.Assert(Boolean condition, System.String message). This is an Assert in the EF code, so looks like a bug in the EF. After looking at the EntityKeyElement.Validate method I could easily find the Assert:

Debug.Assert(
    property != null,
    "This should never be null, since if we were not able to resolve, we should have never reached to this point");

This was a good find (bug filed: https://entityframework.codeplex.com/workitem/1038) but now – how this could have happened? After poking around and using less than scientific methods to debug this I finally concluded that the error is in the invalid SSDL file stored in the __MigrationHistory table. Here is the most interesting fragment:

<EntityType Name=""HistoryRow"" p5:IsSystem=""true"" xmlns:p5=""http://schemas.microsoft.com/ado/2012/10/edm/migrations"">
  <Key p5:IsSystem=""true"">
    <PropertyRef Name=""MigrationId"" p5:IsSystem=""true"" />
    <PropertyRef Name=""ContextKey"" p5:IsSystem=""true"" />
  </Key>
  <Property Name=""MigrationId"" Type=""varchar"" MaxLength=""255"" Nullable=""false"" p5:IsSystem=""true"" />
  <Property Name=""ContextKey"" Type=""varchar"" MaxLength=""512"" Nullable=""false"" p5:IsSystem=""true"" />
  <Property Name=""Model"" Type=""longblob"" Nullable=""false"" p5:IsSystem=""true"" />
  <Property Name=""ProductVersion"" Type=""varchar"" MaxLength=""32"" Nullable=""false"" p5:IsSystem=""true"" />
</EntityType>

It does not look unusual but according to the schema for the SSDL the Key element must not have any attributes (be it in empty or non-empty namespace). Why this thing worked on the .NET Framework and not on Mono? Apparently the Xsd validation in Mono works a bit differently than on the .NET Framework. When processing PropertyRef elements the Xsd validator on .NET Framework reported they were valid whereas Mono claims they are not. I am not sure who is right in this case and, honestly, I don’t care too much. The major bug is in the EF – we should not produce invalid SSDL artifacts (bug filed: https://entityframework.codeplex.com/workitem/1038). This can be fixed either by not writing the {http://schemas.microsoft.com/ado/2012/10/edm/migrations}IsSystem attribute or by allowing attributes in non-empty and non-EF schema on the element. Since I already had a bootleg of entityframework.dll (and I believe the bug is actually in the schema for SSDL) I decided to fix the schema. In the System.Data.Resources.SSDLSchema_3.xsd file I changed the TEntityKeyElement to look like this (added the xs:anyAttribute line):

<xs:complexType name="TEntityKeyElement">
  <xs:sequence>
    <xs:element name="PropertyRef" type="edm:TPropertyRef" minOccurs="1" maxOccurs="unbounded" />
    <xs:any namespace="##other" processContents="lax" minOccurs="0" maxOccurs="unbounded" />          
  </xs:sequence>
  <!-- added to allow attributes on the element-->
  <xs:anyAttribute namespace="##other" processContents="lax" />
</xs:complexType>

This almost did the trick – I went past the NullReferenceException… but only to hit an exception with “Failed for unknown reasons” along with “SqlServer” in the message:

Could not load signature of System.Data.Entity.SqlServer.SqlProviderServices:GetExecutionStrategy due to: Failed for unknown reasons.

Unhandled Exception:
System.TypeInitializationException: An exception was thrown by the type initializer for ExtentPlaceholderCreator ---&gt; System.TypeLoadException: Could not load type 'System.Data.Entity.SqlServer.SqlProviderServices' from assembly 'EntityFramework.SqlServer, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'.
  at (wrapper managed-to-native) System.MonoType:GetPropertiesByName (System.MonoType,string,System.Reflection.BindingFlags,bool,System.Type)
...

Hello??? Anyone there??? I am using MySQL. Any reason you want to load the SqlServer provider? (Yes, the provider was installed by default when I installed the EF package and then, somewhere on my way (must have been after making the app work on .NET Framework), I removed it manually but there should not be a reason to have this provider in the project (and potentially deployed) since it is not being used in my app). This seems like a bug to me but it might have been fixed since alpha2 (a similar issue like this has already been fixed http://entityframework.codeplex.com/workitem/587). Just in case I filed a bug for this https://entityframework.codeplex.com/workitem/1039. To make EF happy I did add the provider for SqlServer and… TADA the app worked.
Just in case – here is the “app” (not even worth putting on github)


namespace EF6MySqlTest
{
  public class Customer
  {
    public int Id { get; set; }
    public string Name { get; set; }
  }

  public class SimpleContext : DbContext
  {
    public SimpleContext()
      : base ("name=MySqlTest")
    {}
    
    public DbSet<Customer> Customers { get; set; }
  }

  class Program
  {
    static void Main(string[] args)
    {
      using (var ctx = new SimpleContext())
      {
        ctx.Customers.Add(new Customer() { Name = "moozzyk" });
        ctx.SaveChanges();
      }

      using (var ctx = new SimpleContext())
      {
        foreach (var c in ctx.Customers)
        {
           Console.WriteLine(c.Name);
        }
      }
    }
  }
}

and the config file:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <configSections>
    <!-- For more information on Entity Framework configuration, visit http://go.microsoft.com/fwlink/?LinkID=237468 -->
    <section name="entityFramework" type="System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection, EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" />
  </configSections>
  <startup>
    <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
  </startup>
  <entityFramework>
    <providers>
      <provider invariantName="Devart.Data.MySql" type="Devart.Data.MySql.Entity.MySqlEntityProviderServices, Devart.Data.MySql.Entity, Version=7.6.217.6, Culture=neutral, PublicKeyToken=09af7300eec23701">
        <migrationSqlGenerator type="Devart.Data.MySql.Entity.Migrations.MySqlEntityMigrationSqlGenerator, Devart.Data.MySql.Entity, Version=7.6.217.6, Culture=neutral, PublicKeyToken=09af7300eec23701" />
      </provider>
    </providers>
  </entityFramework>
  
  <system.data>
    <DbProviderFactories>
      <remove invariant="Devart.Data.MySql" />
      <add name="dotConnect for MySQL" invariant="Devart.Data.MySql" 
           description="Devart dotConnect for MySQL" 
           type="Devart.Data.MySql.MySqlProviderFactory, Devart.Data.MySql, Version=7.6.217.0, Culture=neutral, PublicKeyToken=09af7300eec23701" />
    </DbProviderFactories>
  </system.data>
  
  <connectionStrings>
    <add name="MySqlTest" connectionString="server=localhost;user id=moozzyk;password=XXXXXXXXXXXXX;database=TestDb;persist security info=True" providerName="Devart.Data.MySql" />
  </connectionStrings>
</configuration>

To sum up – it was a pretty bumpy road but I am glad I took it. I found a few real issues we should be able to fix before we ship the final version of EF6 and therefore the experience of using EF6 and Mono should be much smoother.