Filter user records using extension methods
Hey, before you start reading! I am in the market, looking for new freelance employment opportunities. If you need assistance on any of your ASP.NET Core projects, I am available for hire for freelance work.
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.