Separating Data Access in Asp.Net Core 2

In Asp.Net Core 2, like in previous incarnations of Asp.Net there is a wizard that gives you a head-start with a a simple user log-in / registration system:

If you set-up a new project using the wizard to create an individual user account, you may notice in the generated project, the lack of seemingly any code to achieve this. The reason being that all the code for the identity system is tucked away inside the razor pages. I see this as mainly a good thing, but with one exception*: I don’t like having the DB access code inside the main web project; it makes DI very difficult. So this is the story of how you can extricate the DB Access portion of this into a separate project.

Context

The crux of this is to move the context into a separate project; so let’s start with a new project:

If you just want the identity access, then you’ll only need to move the
ApplicationIdentityDbContext, however, in real life, you’re probably going to end up with two contexts:

The contexts themselves need to be separate because the identity context inherits from IdentityDbContext**:

public class ApplicationIdentityDbContext : IdentityDbContext
{
    
    public ApplicationIdentityDbContext(DbContextOptions<ApplicationIdentityDbContext> options)
        : base(options)
    {
    }
}

Your second context should just inherit from DbContext.

NuGet

There’s a couple of gotcha’s with this; but the libraries that you need in the DataAccess project are:

Install-Package Microsoft.EntityFrameworkCore
Install-Package Microsoft.EntityFrameworkCore.SqlServer
Install-Package Microsoft.Entensions.Identity.Stores

Startup.cs

Finally, you’ll need to change the DI to register both contexts:

services.Configure<IdentityOptions>(options =>
{
    options.Password.RequireNonAlphanumeric = false;                
});
services.AddDbContext<ApplicationDbContext>(options =>
    options.UseSqlServer(
        Configuration.GetConnectionString("DefaultConnection")));
services.AddDbContext<ApplicationIdentityDbContext>(options =>
    options.UseSqlServer(
        Configuration.GetConnectionString("DefaultConnection")));
 
services                
    .AddDefaultIdentity<IdentityUser>()
    .AddEntityFrameworkStores<ApplicationIdentityDbContext>();

I’m using SqlServer here, so if you’re not then you’ll obviously need to change the bits around that. You’ll notice that I switched the requirement to have your password have a non-alphanumeric character – especially for development, this can be annoying. I also don’t necessarily accept that it increases security for the site***.

Migrations

Now that you have multiple contexts, when you add a migration, you’ll need to specify the context to use; for example:

Add-Migration "InitialUserSetup" -context ApplicationIdentityDbContext

The same is true for Update-Database:

Update-Database -context ApplicationIdentityDbContext

Footnotes

* Okay – there may be other pitfalls; but if this works for 60% of the authentication cases, why not have it all inside a magic black box? When you need something more customised, you can always rip this out and replace it with your own.

** There’s nothing stopping you having the main DbContext inherit from IdentityDbContext, or just using IdentityDbContext as the main context.

*** Obviously, it does improve security for the site if everyone is using a 20 digit code and they start using non-alpha-numeric characters in that code; however, if they’re using a password manager, they probably are already generating such a code, and if not then you’ll just force “Password123” to “!Password123”, so you probably don’t gain much!

References

https://github.com/aspnet/EntityFrameworkCore/issues/7891

An excellent intro to Asp.Net Core 2 default structure

5 thoughts on “Separating Data Access in Asp.Net Core 2

  1. Neil McKechnie

    This is helpful, thanks so much for the article. But when I try have my new Identity context inherit from IdentityDbContext, it is not found in any of the namespaces you noted to be added to the Data project.

    Since I don’t want to add using Microsoft.AspNetCore.Identity.EntityFrameworkCore to my Data project (violates separation of concerns prompting me to separate these projects in the first place), can you help me figure out where to get the reference to IdentityDbContext please?

    Reply
    1. pcmichaels Post author

      Thanks for your question.

      The class itself is here:

      https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.identity.entityframeworkcore.identitydbcontext?view=aspnetcore-2.1

      So it looks like you haven’t got a huge amount of choice 🙂

      In my post, my target was to separate the data access from the main project; are you trying to break up the data access layer into silos? If that’s the case then I, personally, don’t see why the silo relating to identity couldn’t reference EF Core Identity. Arguably, if this is the case, then you might even want them in separate data stores anyway.

      If you could tell me a little more about your target architecture, I may be able to offer a more specific suggestion.

      Reply
  2. Neil McKechnie

    Thanks so much for your response. I think you are right that there is not much other choice than to reference Microsoft.AspNetCore.Identity.EntityFrameworkCore in the Data layer.

    That’s not ideal only since AspNetCore is a UI technology and in a perfect world your Data layer has no dependencies on any UI namespaces / DLLs.

    I was hoping there might be an equivalent IdentityDbContext class in a pure EntityFrameworkCore namespace that is not a child of AspNetCore. Microsoft probably could have done this but it seems a little lazy of them to jam everything into AspNetCore on the assumption that solutions would only have one project and not separate out the Data layer.

    Reply
    1. pcmichaels Post author

      I think that there are forces within MS that no longer see (or maybe never have seen) the advantage of having a data access layer separate. I strongly suspect that as EF progresses, it in itself is seen as the separation.

      I’m not sure that Asp.Net Core can be seen as a UI technology anymore, though… for example, there’s no longer a separate WebApi concept as there was in MVC 5/6.

      Reply
  3. Neil McKechnie

    Good points indeed.

    As far as WebAPI being a part of Asp.Net Core, I think of it as just another way to emit data via JSON (or XML), as opposed to HTML. From that perspective it is more of a UI layer than a data layer, whereas the Data layer is the only thing interacting with the database.

    In my solutions, I consider the API project to be a peer of the Web project, both with dependencies on the Data layer and the Domain layer. Especially if you’re using Razor for the Web layer (and not AngularJS or something like it that would consume the API directly), then Web layer isn’t using the API…they really are more like peers. For the most part, my API emits the same ViewModels that my Web/Razor project gets from the Controllers.

    This is what inspired me initially: https://www.infoq.com/articles/advanced-architecture-aspnet-core

    Anyways, to each their own, and thanks again for the original posting.

    Reply

Leave a Reply

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

This site uses Akismet to reduce spam. Learn how your comment data is processed.