In this guide, I will show you how to use the current version (8.2.1) of the EF Core Provider for mongoDB with its limitations.
The limitations that prevent this provider from directly being used for ASP.NET Core Identity include lack of support for the Select projection and some other Linq operations, which are used in Identity. Therefore some extra steps are needed to achieve an operational program.
Prerequisites
You need the following to complete follow along.
- Mongo Atlas account and a database URL for your cluster
- Configured development Environment that can develop Blazor Server App (I will be using Visual Studio 2022)
- An active internet connection (I guess it's obvious but anyway)
Creating the project
In Visual studio, Create a Blazor Server application (I have named min WeatherApp
) and choose .NET version to 8.0 LTS
and Athentication type
to Individual Account
. For Interactive render mode
, select Server
and choose your Global
Interactivity location
After creating the application, you will notice that it has been configured to use SQL server by Default as the storage for EF Core in the Program.cs
file. We shall change this shortly
var connectionString = builder.Configuration.GetConnectionString("DefaultConnection") ?? throw new InvalidOperationException("Connection string 'DefaultConnection' not found.");
builder.Services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(connectionString));
builder.Services.AddDatabaseDeveloperPageExceptionFilter();
Adding MongoDB related configurations
We shall start by installing the required nugget packages.
Use any preferred method to install the package MongoDB.EntityFrameworkCore
version 8.2
.
In the appsettings.json
file define an object to store the MongoDB settings as shown below
"MongoDBSettings": {
"AtlasURI": "mongodb+srv://<username>:<password>@cluster0.abc.mongodb.net/?retryWrites=true&w=majority",
"DatabaseName": "weatherapp"
}
For the AtlasURI
key, use the URI from the cluster that you created in your MongoDB Atlas account.
Also create a MongoDBSettings
class in the Data
folder in the root of the project to be used in mapping the settings.
public class MongoDBSettings
{
public string AtlasURI { get; set; } = string.Empty;
public string DatabaseName { get; set; } = string.Empty;
}
In the Program.cs
file, there is a section of code used to configure the database context to use SQL server as shown below.
Replace that code with the code below, that uses MongoDB provider.
builder.Services.Configure<MongoDBSettings>(
builder.Configuration.GetSection("MongoDBSettings"));
var mongoDBSettings = builder.Configuration.GetSection("MongoDBSettings").Get<MongoDBSettings>();
builder.Services.AddDbContext<ApplicationDbContext>(options =>
options.UseMongoDB(mongoDBSettings!.AtlasURI ?? "", mongoDBSettings.DatabaseName ?? "")
.EnableSensitiveDataLogging());
builder.Services.AddDatabaseDeveloperPageExceptionFilter();
Using the MongoDB ObjectId
as the primary key type for ASP.NET Core Identity
By default, ASP.NET Core Identity uses a GUID string as the primary key while storing users in the SQL databases. However, it allows you to customize this and use another type like long, ulong, int, etc. In this case, since our databases uses its on unique identifier as the ObjectId
, we shall use this for identity. Let do the necessary modifications to ensure this.
Note: Remember to add the necessary using statements in each file where we have modified the code.
Firstly, modify the ApplicationUser
class in the Data
folder to use the ObjectId as the primary key type for the identity user.
public class ApplicationUser : IdentityUser<ObjectId>
{
}
In the same Data
folder, modify the ApplicationDbContext
class to also inherit from a version of IdentityDbContext
that allows specifying a different type for the primary key of the user and roles. We shall use ObjectId
for both.
public class ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
: IdentityDbContext<ApplicationUser, IdentityRole<ObjectId>, ObjectId>(options)
{
}
At this moment, we are able to run the application and a user can be registered. But when we try some other operations like the default account confirmation, we get exceptions notifying us that the conversion from the string type to the ObjectId type is not supported.
This prevents us from using other identity features. In order to handle this, and the other limitations of the EF Core Provider for MongoDB, we shall need to use a custom store for both users and roles.
Creating the custom stores for identity
In the project, create a new folder and name it CustomIdentityStores
and create 2 files, namely UserStore.cs
and RoleStore.cs
Get the starter code from the identity repository for each of these files using the links below (from the official identity repository)
Apart from the namespace declaration, paste the rest of the code into the corresponding file and we deal with the modifications (use local namespace).
In the UserStore.cs we shall have one error corresponding to some resource that we don't have access to.
For simplicity, just replace it to a constant string(e.g. "Role Not Found")
Modify the custom stores to handle Id conversion and other limitations of the provider
UserStore.cs
In this file, replace the FindByIdAsync
method by the code below, which uses the ObjectId.Parse
method instead of the identity one.
public override Task<TUser?> FindByIdAsync(string userId, CancellationToken cancellationToken = default(CancellationToken))
{
cancellationToken.ThrowIfCancellationRequested();
ThrowIfDisposed();
var id = ObjectId.Parse(userId);// ConvertIdFromString(userId);
return UsersSet.FindAsync(new object?[] { id }, cancellationToken).AsTask();
}
Also modify the default User Store type and the corresponding other implementations to use ObjectId
as the type of the unique identifier for both User and Role entities.
Make sure that you replace the DBContext
injected in the default implementation to use the one ApplicationDbContext
of our application.
In addition, methods such as GetRolesAsync
where unsupported features such as the select projection are used, we can use client-side evaluation if we don't have a lot of roles or use any techniques of your choice to convert the Linq to SQL logic to something that is supported by the provider.
For example the code shown below:
can be converted to.
Do this for all such cases (e.g. in the GetClaimsAsync
method) and end up with the changes as shown in the complete file here.
RoleStore.cs
In this file, follow the same steps as in the UserStore.cs
. The example result is shown the completed file here.
Register the new Stores
In the Program.cs
file, register these classes to be used by identity instead of the default stores by using the code below.
builder.Services.AddTransient<IUserStore<User>, UserStore>();
builder.Services.AddTransient<IRoleStore<IdentityRole<ObjectId>>, RoleStore<IdentityRole<ObjectId>>>();
Once Everything is wired correctly, You should be able to run the application, register, login and view the authorized resources.
Find the completed code in a git hub repository here.
Enjoy coding without any limits.
Top comments (0)