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!

Advertisements

5 thoughts on “Raspberry Pi + Mono + EF6 – defeat!

  1. […] running Vice installed from http://ftp.uk.debian.org it just dies with the Segmentation Fault error. After my last battle with Raspberry Pi, Mono and EF6 I think it might be possible to install the package from http://ftp.uk.debian.org on the Soft-float Debian […]

    Like

  2. Rpi Experimentator says:

    It seams that mono is broken if you installed it via apt-get on armhf. Maybe you’ll have better luck with the package (mono 2.11.4 correctly patched for armhf) found in this thread:
    http://www.raspberrypi.org/phpBB3/viewtopic.php?f=34&t=37174

    Like

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: