Using Dapper.CX with Dependency Injection - adamfoneil/Dapper.CX GitHub Wiki

Nuget

This topic describes a couple ways to use Dapper.CX with dependency injection in ASP.NET Core.

First, install NuGet package Dapper.CX.SqlServer.AspNetCore in your application project.

The simplest case -- no user integration

During Startup you add the DapperCX service with a connection string and a delegate for converting SELECT SCOPE_IDENTITY() to your entity identity type. This example uses int Id types and a connection string retrieved from configuration:

public void ConfigureServices(IServiceCollection services)
{
    var connectionString = Configuration.GetConnectionString("Default");
    services.AddDapperCX(connectionString, (value) => Convert.ToInt32(value));
}

Now, in your pages or controllers, you'd inject it like this:

public class SampleController : Controller
{
    private readonly DapperCX<int> _data;

    public SampleController(DapperCX<int> data)
    {
        _data = data;
    }
}

In your Razor pages or Blazor components, you'd inject it like this:

@inject DapperCX<int> Data

Now you have access to all DapperCX methods but without the User access. We'll address that in the next example.

Setting the User Property

Most applications will have authentication and need to track crud operations by user in some way. You can add your user profile model during Startup to DapperCX so that the User property is automatically populated with info about the current user. When you do that, you have access to a lot of functionality via interfaces you can opt into.

The only requirement before you begin is that your user profile model class must implement IUserBase or IUserBaseWithRoles. The sample app in this repo has this example. You can also use AO.Models.UserProfileBase as the basis for your own profile class. This advantage with this is that it comes with a local time implementation.

To set the User property, use this overload of the AddDapperCX method. You supply a delegate Func<IServiceProvider, TUser> getUser that returns your TUser user profile instance. There are a couple built-in IServiceProvider extension methods you can use for this: GetAspNetUser and GetAspNetUserWithRoles. The following example uses GetAspNetUser, but GetAspNetUserWithRoles would work also. This example uses the UserProfile model class from this repo's Sample App, and is adapted from here.

 services.AddDapperCX(
     connectionString, 
     (sp) => sp.GetAspNetUser<UserProfile>(connectionString), 
     (id) => Convert.ToInt32(id));

You can of course provide your method that returns your TUser instance. This would be necessary if you need to initialize users in a way unique to your application.

When your TUser type is provided in this way, you inject in components like this:

@inject DapperCX<int, UserProfile> Data

Remember to use your own TUser model type. In this example, it's UserProfile.

Using ISession to reduce database traffic

When you add the DapperCX service, it's a scoped service, meaning it's initialized anew for each HTTP request. When you use a method like GetAspNetUser to query the user profile, that query executes with every single page view in your application. This can hurt your app's performance at scale. You can mitigate this by using an ISession provider that will fetch user profile from storage you provide.

The built-in methods GetAspNetUser and GetAspNetUserWithRoles have an optional sessionKey argument. If you have an ISession service registered and you provide the optional sessionKey argument, DapperCX will try to use your ISession service to get user profile data before querying the database. An example might look like this. The ISession service here is UserAppData. Note also that the sessionKey argument is a string literal "profile".

services.AddScoped<ISession>((sp) => new UserAppData(sp));

services.AddDapperCX(
    connectionString, 
    sp => sp.GetAspNetUserWithRoles<UserProfile>(connectionString, "profile"),
    (id) => Convert.ToInt32(id));

The main caveat to bear in mind when using ISession like this is in highly permission-sensitive applications. If someone's roles changes, your session storage won't be aware of the change until they log out and back in again. So, you may need to implement a way for admins to force a user to logout, or using ISession might not be a good fit for your application.

Limitations of Roles

Although you can use a method like GetAspNetUserWithRoles to query the roles from the built-in ASP.NET Identity table, this doesn't integrate with the built-in [Authorize] attribute nor AuthorizeView component in Blazor. That said, you can still implement role-based logic in your applications:

  1. On your TUser type used with the AddDapperCX startup method, implement IUserBaseWithRoles. Example: UserProfile.

  2. IUserBaseWithRoles has just one member Roles. I added a helper method HasRole to make checking for the presence of a role easier.

  3. In your Startup method in the AddDapperCX call, GetAspNetUserWithRoles as in this example.

Now anywhere in your application where DapperCX is injected, you can query roles like this Razor page example:

@if (Model.Data.User.HasRole("Admin"))
{
    <p>I have the <code>Admin</code> role!</p>
}
⚠️ **GitHub.com Fallback** ⚠️