Back to all posts

Entity Framework Core Complex Projects

Posted on Aug 25, 2017

Posted in category:
Development
Entity Framework

For those migrating to Entity Framework Core (EFCore), the online documentation is very helpful for the most basic of projects. However, as you work to create more complex applications, things can get more complicated. One area I have witnessed being a concern is how to manage the extraction of your EFCore or Identity DBContext into a reusable project. This post will look at this process, and the steps necessary to properly manage interactions with migrations.

Creating a Data Project

Regardless of your existing project setup, the first step is to create a new .NET Core Class Library project. In many cases, I will name this project using the format ProjectName.Data, or similar. Naming doesn't exactly matter at this time, just create the new project.

With the project, you will want to add the needed NuGet packages to add support for Entity Framework. If you desire to use ASP.NET Identity, you will have an additional package to install. The following is the list of minimum packages to include.

  • Microsoft.EntityFrameworkCore
  • Microsoft.EntityFrameworkCore.Design
  • Microsoft.EntityFrameworkCore.SqlServer
  • Microsoft.EntityFrameworkCore.SqlServer.Design

With these packages installed, you can now migrate your Model classes as well as your DB Context to this new project. After completing this step, you will want to add a reference to your Data project from the web project and correct any references. Your application should compile and run at this point, but don't stop here!.

Helping with Initialization

Although you could stop here, I find that when working with a referenced project it is often nice to bring initialization code into your project as well. This way in your web project or a console project, a single call to ConfigureDataAccess() can result in the proper initialization of all needed objects. The simplest route to accomplish this is to add an extension method and place your initialization code inside. The following snippet demonstrates this, assuming a DBContext name of ProjectXDbContext.

Startup Code
public static class StartupExtensions
{
    public static void RegisterProjectXDataServices(this IServiceCollection services,
        IConfigurationRoot configuration)
    {
        // Configure the DB Context
        services.AddDbContext<projectxdbcontext>(options =>
            options.UseSqlServer(configuration.GetConnectionString("DefaultConnection")));

        //Additionally configure Identity
        services.AddIdentity<userprofile, userrole="">()
            .AddEntityFrameworkStores<projectxdbcontext, int="">()
            .AddDefaultTokenProviders();
    }
}

As you can see here, this is the same code that you might have already included in your startup.cs. Now that you have this extension method, you can simply call 'services.RegisterProjectXDataServices' in the startup.cs.

But You Mentioned Issues?

This all sounded simple, right? It is pretty simple to get to this point, and if you don't have to make any changes, migrations, or database updates, you might not notice anything out of the ordinary. But the first time you try to run a command you will see some differences. Attempting something such as the following will result in an error being displayed.

dotnet ef migrations add TestMigration

Depending on your current working directory, the exact error might differ. If running from the context of the web project, it will most likely give you an error about not being in the correct location. If you call it from the Data project, it might give you an error, or based on recent testing with the latest tooling, could result in the dotnet process crashing!

Getting Around The Issues

The solution to getting around the issue is quite simple! We just need to follow two simple rules.

  • Always execute 'dotnet ef' commands when in the data project directory
  • Always include a ''--startup-project" parameter with our ef commands

In our example, we have a solution folder with two projects, ProjectX.Data and ProjectX.Web. In the end, from the command line, we can execute the following command to add a new migration. This will use the proper startup directory, will read the proper configuration files, and be able to operate as expected.

dotnet ef --startup-project ../ProjectX.Web migrations add Initial

This process tells the ef tooling that we want to run our migrations, but we want to focus on using the Web Project as our initialization or startup project. This allows your configuration to be loaded, the database connection to be found and all operations to succeed. Migrations will still be stored inside the Data project.

Conclusion

With a little bit of planning, it is easily possible to extract your data tier, including Identity, out to an external project, allowing easy re-use across multiple projects. There are just a few items that are less than clear when you start down this road. I hope that this helps you save some time!