Extricating AspNetCore Identity from Entity Framework Database for Xamarin

Unison.School has become a very large project. I’m proud of the hundreds of teachers who are using it in their schools, and hope to keep building it even larger.

I built Unison.School while learning the cutting-edge Asp.Net Core 2.0 framework, including the EntityFrameworkCore database ORM. Based on the starting templates for a web app, I built my Teacher and Student user models inheriting from the IdentityUser class. This provides lots of wonderful built-in functionality around logging in and authorizing access to specific pages.

In the year since laying out those login procedures, the model for the project has grown to over 100 entities in the database. A teacher, for example, could be connected to all the following entities:

Each of these, in turn, would be tied to even more tables. This was all fine and good until I looked to create a mobile app version of the service.

As you may know, Xamarin allows .Net programmers to share code with and across mobile platforms. What you may not have realized, if you are new to .Net and follow a path similar to mine, is that IdentityUser is not compatible with Xamarin. Yet I wanted to use many of these same classes in my mobile app, and it seemed ridiculous to create two copies of each entity, and have to maintain the code between them.

The model library included a link to the Nuget package for Microsoft.AspNetCore. EntityFrameworkCore.Identity, so I needed to move any code I wanted to reuse into a new portable class library, and reference it from both the Asp.Net Core app and the Xamarin app.

My first attempt to disentangle the model was to create an IApplicationUser interface, which both my Asp.Net Core ApplicationUser (base class for Teacher and Student) and a new mobile version could inherit from. I copied all the code that I cared about into this interface.


        public interface IApplicationUser
        {
            string Id { get; set; }
            string FirstName { get; set; }
            string LastName { get; set; }
            string UserName { get; set; }
        }
    

Next, I realized that I also needed to create new classes for Teacher and Student that did not inherit from IdentityUser. So I similarly created ITeacher and IStudent.

So far, so good. But now, let’s take the teacher’s LessonPlan object as an example. In the Teacher class we have:


        public List LessonPlans { get; set; }
    

And in the LessonPlan class:


        public string TeacherId { get; set; }
        public virtual Teacher Teacher { get; set; }
    

In the DbContext builder, the two are connected:


        builder.Entity()
            .HasOne(lp => lp.Teacher)
            .WithMany(t => t.LessonPlans)
            .HasForeignKey(lp => lp.TeacherId);
    

If I moved LessonPlan into the shared library, it could not reference Teacher. The same was true for all the other entities that had relationships with my user classes.

I initially thought I should perservere with a strategy I’ll call “Interface All the Things!” I created an ILessonPlan, and dozens of other interfaces. Each entity then referenced the interfaces, instead of the base classes. However, I discovered when moving back to the DbContext that this approach would not work. Entity Framework relationships must be between concrete classes, not interfaces. It is possible to cast a single object to it’s instantiation:


        builder.Entity()
            .HasOne(b => (Teacher)b.Teacher)
            .WithMany(t => t.LessonPlans)
            .HasForeignKey(b => b.TeacherId);
        // Casts ITeacher to Teacher
    

But this is not possible to do with a List<object>, which describes one side of all my one-to-many relationships.

I was definitely stumped for a long time, and could find no help on the internet for this scenario. Finally, while listening to my favorite podcast, Coding Blocks, they mentioned in one episode removing unneeded relationships, especially dual-directional ones! Since EF and all the tutorials show how to make the connections, it never occured to me not to connect two objects that I didn’t need to follow with a query!

Once I understood this, implementing was simple (although time-consuming). In the LessonPlan example, I removed the property public Teacher Teacher { get; set; }, but left the public int TeacherId { get; set; }. I was also able to delete the connection in the DbContext builder. This allowed me to move my LessonPlan and most other entities into the shared library, since there was no direct connection to the user classes.

A large portion of the time was then spent tracking down everywhere I had used a call such as lessonPlan.Teacher. While these were less common than calling teacher.LessonPlans, the need did arise. So now, in such instances, I did have to add one step:


        var teacher = DbContext.Teachers.Single(t => t.Id == lessonPlan.Id);
    

This extra line of code was a small price to pay to reuse my 100 classes in a new project! In the Xamarin project, you can also create a nearly identical¬†DbContext, inheriting from the DbContext base class instead of from IdentityDbContext. I’d also like to point out how useful ReSharper is for a refactoring of this magnitude. It will automate a lot of namespace changes, search for usings, etc. I can’t imagine how long it would have taken me without that tool.

Leave a Reply

Your email address will not be published. Required fields are marked *