EF6 on Xamarin: Progress (or lack thereof)

(This entry is part of a series. The audience: SQL Server developers. The topic: SQLite on mobile devices.)


Yes, actually I DO want that

"I want Entity Framework on Xamarin."

Variations of this remark show up regularly in various online forums. The typical response is something like: "You don't want Entity Framework on a mobile device. It's too big and heavy. You should just use sqlite-net."

I hate this reply, but it has [more than] a grain of truth in it. Yes, sqlite-net is very cool, and a much better fit for mobile use cases. Yes, Entity Framework is big and heavy.

But I would bet a dollar that at least one person has claimed that "EF is too big for mobile" and then proceeded to play "Star Wars: Knights of the Old Republic" on their iPhone.

Besides, some people have a a big pile of EF code and need to port it for a mobile app. There are valid reasons to want EF on Xamarin.

Recently I decided to get my hands dirty and try to make this work. I have not succeeded. Yet. But I've made some progress and reached a good place to report my findings. Maybe other people working on this will find some useful information here.

The Goal

  • I'd like to see EF6 code running on Xamarin.iOS and Xamarin.Android.

  • Tooling doesn't matter much. I'm not expecting Xamarin Studio to show me lines and boxes for my model.

  • I would even declare some measure of victory if I had a solution which handled only one of EF's four workflows. I focused on "Code First with Existing Database".

  • The only provider that matters is SQLite. I focused on System.Data.SQLite.

The Challenge

There are basically four different implementations of the platform:

  • .NET is, well, the full platform. The "official" one. From Microsoft.

  • Mono is an open source implementation. It has most everything in .NET. But not all.

  • Xamarin.Android is Mono for Android. It has a lot of Mono, but not all. (Most notably, System.Configuration is missing.)

  • Xamarin.iOS is Mono for iOS. It has even less of the stuff in Mono than the Android version does. (Most notable, it's an AOT compiler.)

I focused on Xamarin.Android.

Two clarifications before I proceed

  • I'm a huge fan of Xamarin. Anything here which looks like criticism is just me being objective about the weaknesses and tradeoffs of their still-very-awesome product suite.

  • Julie Lerman's dog probably knows more about Entity Framework than I do.

Starting point

Entity Framework was open sourced in 2012, and shortly thereafter was made part of Mono 2.11.3.

For a little while, I proceeded under the delusion that I would not have to make any changes to the Entity Framework code. That didn't last long.

When I started hacking and tearing things apart, I began here.

I had no problem building the tree on Windows. With a few small changes to the build files (remove all use of $([MSBuild]::GetDirectoryNameOfFileAbove())), I was able to build on my Mac using:

xbuild EntityFramework.csproj

System.Configuration

Perhaps the biggest issue is the lack of System.Configuration in both of the Xamarin platforms.

Xamarin dude Jonathan Pryor says:

"The problem with System.Configuration is that once it's in the door, the entire XML stack comes along for the ride, and the linker can't remove it because it's used from ~everywhere. System.Xml.dll is 1.2MB, so that would be at least a 1.2MB increase to minimum app sizes."

This is a huge problem for Entity Framework, which uses the app.config file all over the place. I tried several different approaches to deal with this:

  • First I tried to just remove all the code which uses System.Configuration, starting with a hacked up version of AppConfig.cs. Then I realized what a huge change that was, so I reverted and started over.

  • Then I tried to just stub in a minimal, non-functional implementation of only the parts of System.Configuration that I needed to make the build succeed. Then I realized what a huge job that was, so I reverted and started over.

  • Then I tried to bring in Mono's System.Configuration.dll and make it part of my app. This would be the preferable solution anyway, since it would require far fewer code changes to the Entity Framework code while preserving some ability to use an app.config file. But I couldn't get this to work, and somebody at Xamarin support expressed significant pessimism about this path.

  • Finally, I just removed all references to System.Configuration, including AppConfig.cs and everything that references it. This solution is far from ideal, but it did get me moving on to the next problem.

System.Data.Common.DbProviderFactories

Xamarin doesn't support this either, largely for the same reason. It's really just another way of reading an XML config file.

This function is used in a few places in the Dependency Resolution part of Entity Framework.

Right about here is when I got seriously tempted to just remove all the Dependency Resolution code completely. After all, I only care about one ADO.NET provider. Why do I need this big and ultra-powerful config system which is mostly designed to support a diversity of providers that I don't care about?

I talked myself off this ledge before too long. I remove the uses of System.Data.Common.DbProviderFactories, replacing them temporarily with stuff like "return null".

Subclass DbConfiguration

My rationale for removing all these configuration capabilities was that I hoped they would not be necessary if I gave my DbContext subclass an actual connection instead of a connection string.

    public class BloggingContext : DbContext
    {
        public BloggingContext(DbConnection c) : base(c, false)
        {
        }

        public DbSet Blogs { get; set; }
        public DbSet Posts { get; set; }
    }

...

        var sb = new System.Data.SQLite.SQLiteConnectionStringBuilder();
        sb.DataSource = Path.Combine(Path.GetTempPath(), "whatever.db");
        string cs = sb.ConnectionString;

        var conn = new System.Data.SQLite.SQLiteConnection(cs);
        conn.Open();

        using (var db = new BloggingContext(conn))
        {
            var blog = new Blog { Name = "thoughts" };
            db.Blogs.Add(blog);
            db.SaveChanges();

            // Display all Blogs from the database 
            var query = from b in db.Blogs
                        orderby b.Name
                        select b;

            Console.WriteLine("All blogs in the database:");
            foreach (var item in query)
            {
                Console.WriteLine(item.Name);
            }
        }

At some point I realized that Entity Framework needs a lot more info about the provider than just the connection string. Julie's dog would have known better.

So I read about Code-Based Configuration. And then I did this:

    public class MyConfiguration : DbConfiguration
    {
        public MyConfiguration()
        {
            SetProviderServices(
                "System.Data.SQLite", 
                System.Data.SQLite.EF6.SQLiteProviderServices.Instance
                );
        }
    }

And that required me to hack the SQLite provider to make System.Data.SQLite.EF6.SQLiteProviderServices public instead of internal, which seems more correct anyway. I think.

AssociatedMetadataTypeTypeDescriptionProvider

For some reason, this class (in System.ComponentModel.DataAnnotations) is not supported by Xamarin. It's intentional. The code on Github shows that file surrounded by:

    #if !MOBILE

But I don't know why.

I ducked this problem by copying a bunch of stuff from that Mono file into my hacked-up code.

Time for a break

This is where I decided to pause. The next problem was an exception being thrown while Entity Framework was trying to ingest SQLiteProviderServices.ProviderManifest.xml. I stared at this for a while, but didn't get past it.

It's Friday. I spent most of the week on this investigation, and now I need to catch up on some other things.

If/when I make more progress on this, I'll post more info about it.

If you make progress, please let me know.

I am hopeful about this link:

Entity Framework Everywhere

Summary

You don't want Entity Framework on a mobile device. It's too big and heavy. You should just use sqlite-net.