Splitting out ASP.NET Core Identity into a separate library
In a recent post I described how to change the data type that ASP.NET Core Identity uses for the generated id's for users. Before I go much further I want to split this out into a separate library, so I can re-use it across multiple web applications with ease. This is of particular relevance as the project I'm working on will be composed of multiple web applications so doing this simplifies handling that later. Getting this done and working now means I can get it right before I'm in a place where I've got to dis-entangle it from a deeper integration later.
Related posts:
- Taking the GUID out of ASP.NET Core Identity
- Splitting out ASP.NET Core Identity into a separate library (this post)
- Extending the ASP.NET Core Identity user (this post)
- Reading and writing custom ASP.NET Core Identity user properties
- Extending the ASP.NET Core Identity UserManager to set the Employee Id during registration
- The finishing touches to hooking into ASP.NET Core Identity user creation
Migrating the ASP.NET Core Identity Code
First up is creating the project, adding it to the solution and also as a reference to the web application. Packaging it up as a NuGet package might come later, but is a bit more complex/overkill than I need right now. So, to the command line!
Creating the project:
D:\Git\RW.HumanResourcesPortal>md RW.HumanResourcesPortal.Identity D:\Git\RW.HumanResourcesPortal>cd RW.HumanResourcesPortal.Identity D:\Git\RW.HumanResourcesPortal\RW.HumanResourcesPortal.Identity>dotnet new classlib The template "Class library" was created successfully. Processing post-creation actions... Running 'dotnet restore' on D:\Git\RW.HumanResourcesPortal\RW.HumanResourcesPortal.Identity\RW.HumanResourcesPortal.Identity.csproj... Restoring packages for D:\Git\RW.HumanResourcesPortal\RW.HumanResourcesPortal.Identity\RW.HumanResourcesPortal.Identity.csproj... Generating MSBuild file D:\Git\RW.HumanResourcesPortal\RW.HumanResourcesPortal.Identity\obj\RW.HumanResourcesPortal.Identity.csproj.nuget.g.props. Generating MSBuild file D:\Git\RW.HumanResourcesPortal\RW.HumanResourcesPortal.Identity\obj\RW.HumanResourcesPortal.Identity.csproj.nuget.g.targets. Restore completed in 339.86 ms for D:\Git\RW.HumanResourcesPortal\RW.HumanResourcesPortal.Identity\RW.HumanResourcesPortal.Identity.csproj. Restore succeeded.
Then it's a matter of adding it to the solution and the Web project:
D:\Git\RW.HumanResourcesPortal>dotnet sln add .\RW.HumanResourcesPortal.Identity Project `RW.HumanResourcesPortal.Identity\RW.HumanResourcesPortal.Identity.csproj` added to the solution.
D:\Git\RW.HumanResourcesPortal>dotnet add .\RW.HumanResourcesPortal.Web reference .\RW.HumanResourcesPortal.Identity
Reference `..\RW.HumanResourcesPortal.Identity\RW.HumanResourcesPortal.Identity.csproj` added to the project.
Now that's all done, which of course could've been done via Visual Studio instead, the first thing to do is to delete the boiler-plate Class1.cs that gets created in the new project by default before copying across all the code files that are directly related to ASP.NET Core Identity. By my reckoning that list is:
- Everything under the \Data folder (so that's the Database context and migrations)
- The two new models I created, ApplicationIdentityUser and ApplicationRole
Through the magic of the new project format, the new project immediately reflects the additional files in Visual Studio and the old project would've done except somehow/somewhere I'd acquired a specific reference to the folders in the project file. Removing that reference got rid of two sad looking folders in Solution Explorer with red crosses adorning them.
Fixing up the Code
The first thing that's very obvious is that the identity code that's been moved to the RW.HumanResourcesPortal.Identity project doesn't compile, because it's missing references that are present in the .Web project. That project only has two references:
These are what're referred to as metapackage's, so if you expand Microsoft.AspNetCore.App what you'll see is a whole host of NuGet packages and not a lot else. The easiest way, by far to pull in the dependencies that the .Identity project requires is going to be to add a reference to the Microsoft.AspNetCore.App metapackage. Attempting to do this via Visual Studi and the NuGet Package Manager, remembering to pick the same version as the .Web project references as there are several later versions, both production and preview, gives up the following error message:
Restoring packages for D:\Git\RW.HumanResourcesPortal\RW.HumanResourcesPortal.Identity\RW.HumanResourcesPortal.Identity.csproj... NU1202: Package Microsoft.AspNetCore.App 2.1.1 is not compatible with netstandard2.0 (.NETStandard,Version=v2.0). Package Microsoft.AspNetCore.App 2.1.1 supports: netcoreapp2.1 (.NETCoreApp,Version=v2.1) Package restore failed. Rolling back package changes for 'RW.HumanResourcesPortal.Identity'. Time Elapsed: 00:00:00.8539106 ========== Finished ==========
Ahh, so it's not going to be quite as simple as I'd hoped! So, rather than take the easy option, I'll just add the packages for Identity. Looking at the list of packages that Microsoft.AspNetCore.App pulls in there are three candidates: Microsoft.AspNetCore.Identity, Microsoft.AspNetCore.Identity.EntityFrameworkCore and Microsoft.AspNetCore.Identity.UI. Based on their descriptions on nuget.org, I've opted to skip the .UI pacakge and take only the first two:
D:\Git\RW.HumanResourcesPortal\RW.HumanResourcesPortal.Identity>dotnet add package Microsoft.AspNetcore.Identity
Writing C:\Users\robertwray\AppData\Local\Temp\tmpF48A.tmp
info : Adding PackageReference for package 'Microsoft.AspNetcore.Identity' into project 'D:\Git\RW.HumanResourcesPortal\RW.HumanResourcesPortal.Identity\RW.HumanResourcesPortal.Identity.csproj'.
log : Restoring packages for D:\Git\RW.HumanResourcesPortal\RW.HumanResourcesPortal.Identity\RW.HumanResourcesPortal.Identity.csproj...
... snip ...
log : Installing Microsoft.Extensions.Identity.Core 2.1.3.
log : Installing Microsoft.AspNetCore.Identity 2.1.3.
info : Package 'Microsoft.AspNetcore.Identity' is compatible with all the specified frameworks in project 'D:\Git\RW.HumanResourcesPortal\RW.HumanResourcesPortal.Identity\RW.HumanResourcesPortal.Identity.csproj'.
info : PackageReference for package 'Microsoft.AspNetcore.Identity' version '2.1.3' added to file 'D:\Git\RW.HumanResourcesPortal\RW.HumanResourcesPortal.Identity\RW.HumanResourcesPortal.Identity.csproj'.
D:\Git\RW.HumanResourcesPortal\RW.HumanResourcesPortal.Identity>dotnet add package Microsoft.AspNetcore.Identity.EntityFrameworkCore
Writing C:\Users\robertwray\AppData\Local\Temp\tmp2ED4.tmp
info : Adding PackageReference for package 'Microsoft.AspNetcore.Identity.EntityFrameworkCore' into project 'D:\Git\RW.HumanResourcesPortal\RW.HumanResourcesPortal.Identity\RW.HumanResourcesPortal.Identity.csproj'.
log : Restoring packages for D:\Git\RW.HumanResourcesPortal\RW.HumanResourcesPortal.Identity\RW.HumanResourcesPortal.Identity.csproj...
... snip ...
log : Installing Microsoft.Extensions.Identity.Stores 2.1.3.
log : Installing Microsoft.AspNetCore.Identity.EntityFrameworkCore 2.1.3.
info : Package 'Microsoft.AspNetcore.Identity.EntityFrameworkCore' is compatible with all the specified frameworks in project 'D:\Git\RW.HumanResourcesPortal\RW.HumanResourcesPortal.Identity\RW.HumanResourcesPortal.Identity.csproj'.
info : PackageReference for package 'Microsoft.AspNetcore.Identity.EntityFrameworkCore' version '2.1.3' added to file 'D:\Git\RW.HumanResourcesPortal\RW.HumanResourcesPortal.Identity\RW.HumanResourcesPortal.Identity.csproj'.
Success! Or at least it would be, if not for the build error "The name 'SqlServerValueGenerationStrategy' does not exist in the current context", but that was easily solved by adding a reference to the package Microsoft.EntityFrameworkCore.SqlServer, as after all, that's the database I'm using to back my identity solution!
Running it up & finishing touches
The $64,000 dollar question is, now I've fixed this all up so it builds, will a login I created previously work? Well.....
Success, again!
The final thing to do is to update the namespacing of the classes in the files that were migrated from the .Web project to the .Identity project, making sure to add a reference to the new namespace for the model classes in ~\Views\_ViewImports.cshtml to avoid some gnarly build errors in generated files. This wasn't quite as plain sailing as I made it out to be, but by changing the namespace of one class at a time, and fixing up build errors triggered from that, success was had.