Filter user records using extension methods

February 12, 2020
ASP.NET Core 3.1

Introduction

Following up from my previous blog post on useful ClaimsPrincipal extension methods I use, I also make use of extension methods for common filters I use when querying my EF database context. One of the scenarios I frequently use these extension methods is when filtering records by the current user.

Filtering data using an extension method

Let assume I have the following entity class defined in my application:

public class RecordedWeight
{
    public int Id { get; set; }

    [Required]
    public DateTime Date { get; set; }

    [Required]
    public decimal Weight { get; set; }

    public string UserId { get; set; }

    [Required]
    public IdentityUser User { get; set; }
}

As you can see, each RecordedWeight entity references an IdentityUser, which is the entity class for users as defined by ASP.NET Identity.

My database context is defined as follows:

public class ApplicationDbContext : IdentityDbContext
{
    public DbSet<RecordedWeight> RecordedWeights { get; set; }

    public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
        : base(options)
    {
    }
}

For each entity that references a user, I define a ForCurrentUser() extension method that adds a where clause to my query to filter the records by the current user.

public static class QueryExtensionMethods
{
    public static IQueryable<RecordedWeight> ForCurrentUser(this IQueryable<RecordedWeight> weights, HttpContext httpContext)
    {
        var userId = httpContext.User.GetUserId();

        return weights.Where(w => w.UserId == userId);
    }
}

BTW, the GetUserId() method is also an extension method I defined, which gets the value of the NameIdentifier claim - which is the current user’s ID when using ASP.NET Identity. I discussed this in the previous blog post.

public static class ClaimsPrincipalExtensions
{
    public static string GetUserId(this ClaimsPrincipal principal)
    {
        return principal.FindFirstValue(ClaimTypes.NameIdentifier);
    }
}

Now, when I want to filter recorded weights by the current user, I use the ForCurrentUser extension method to filter the data. You can see this being used below in a sample Razor Pages PageModel.

public class IndexModel : PageModel
{
    private readonly ApplicationDbContext _dbContext;

    public List<RecordedWeight> Weights { get; set; }

    public IndexModel(ApplicationDbContext dbContext)
    {
        _dbContext = dbContext;
    }

    public async Task OnGetAsync()
    {
        Weights = await _dbContext.RecordedWeights
            .ForCurrentUser(HttpContext)
            .OrderByDescending(w => w.Date)
            .ToListAsync();
    }
}

Using it outside Razor pages

In my example above, I used the extension methods inside a Razor Page, where I had access to the current HttpContext via the HttpContext property of the PageModel class. I commonly make use of MediatR, so in that case, I inject an instance of IHttpContextAccessor into my request handler to get access to the HttpContext, for example:

public class QueryHandler : IRequestHandler<Query, Result>
{
    private readonly ApplicationDbContext _dbContext;
    private readonly IHttpContextAccessor _httpContextAccessor;

    public QueryHandler(ApplicationDbContext dbContext, IHttpContextAccessor httpContextAccessor)
    {
        _dbContext = dbContext;
        _httpContextAccessor = httpContextAccessor;
    }

    public async Task<Result> Handle(Query request, CancellationToken cancellationToken)
    {
        var weights = await _dbContext.RecordedWeights
            .ForCurrentUser(_httpContextAccessor.HttpContext)
            .OrderByDescending(w => w.Date)
            .ToListAsync();

        // ...
    }
}

Conclusion

I this blog post, I demonstrated how you could use extension methods to filter database records by the current user. You can also use IQueryable<> extension methods for any other common filters you want to apply to your database.

You can find the code accompanying this blog post at https://github.com/jerriepelser-blog/filter-user-records-using-extension-methods.

BTW, I believe I first learned about the technique to filter data using extension methods on Ayende’s blog, but unfortunately, I cannot find the blog post in question to link to.

PS: If you need assistance on any of your ASP.NET Core projects, I am available for hire for freelance work.