-
-
Notifications
You must be signed in to change notification settings - Fork 6
EF Core Interceptors
This tutorial will guide you through the process of creating an interceptor in Entity Framework Core that customizes the behavior of saving changes to the database. We'll be creating a SaveChangesInterceptor
that sets metadata on entities implementing a specific interface.
Here is the IOriginatorInfo
interface that will be implemented by the entities you want to intercept.
namespace FairPlayCombined.DataAccess.Interceptors.Interfaces
{
public interface IOriginatorInfo
{
string SourceApplication { get; set; }
string OriginatorIpaddress { get; set; }
DateTimeOffset RowCreationDateTime { get; set; }
string RowCreationUser { get; set; }
}
}
Here is the IUserProviderService
interface that will provide the current user ID and other user-related information.
namespace FairPlayCombined.Interfaces
{
public interface IUserProviderService
{
string? GetCurrentUserId();
string? GetAccessToken();
bool IsAuthenticatedWithGoogle();
}
}
Here is the UserProviderService
class that implements the IUserProviderService
interface.
using FairPlayCombined.Interfaces;
using Microsoft.AspNetCore.Http;
using System.Linq;
namespace FairPlayCombined.Services.Common
{
public class UserProviderService : IUserProviderService
{
private readonly IHttpContextAccessor _httpContextAccessor;
public UserProviderService(IHttpContextAccessor httpContextAccessor)
{
_httpContextAccessor = httpContextAccessor;
}
public string? GetAccessToken()
{
throw new NotImplementedException();
}
public string? GetCurrentUserId()
{
string? result = default;
if (_httpContextAccessor?.HttpContext?.User?.Identity?.IsAuthenticated == true)
{
var claimType = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier";
result = _httpContextAccessor.HttpContext.User.Claims.Single(p => p.Type == claimType)?.Value;
}
return result;
}
public bool IsAuthenticatedWithGoogle()
{
var result = _httpContextAccessor.HttpContext.User
.HasClaim("http://schemas.microsoft.com/ws/2008/06/identity/claims/authenticationmethod",
"Google");
return result;
}
}
}
Here is the SaveChangesInterceptor
class that implements ISaveChangesInterceptor
.
using FairPlayCombined.Interfaces;
using FairPlayCombined.DataAccess.Interceptors.Interfaces;
using Microsoft.Data.SqlClient;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Diagnostics;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace FairPlayCombined.DataAccess.Interceptors
{
public class SaveChangesInterceptor : ISaveChangesInterceptor
{
private readonly IUserProviderService _userProviderService;
public SaveChangesInterceptor(IUserProviderService userProviderService)
{
_userProviderService = userProviderService;
}
public async ValueTask<InterceptionResult<int>> SavingChangesAsync(
DbContextEventData eventData,
InterceptionResult<int> result,
CancellationToken cancellationToken = default)
{
var changedEntities = eventData.Context.ChangeTracker.Entries();
foreach (var entityEntry in changedEntities.Where(e => e.Entity is IOriginatorInfo))
{
if (entityEntry.Entity is IOriginatorInfo entity)
{
var connectionString = eventData.Context.Database.GetConnectionString();
SqlConnectionStringBuilder sqlConnectionStringBuilder = new(connectionString);
var applicationName = sqlConnectionStringBuilder.ApplicationName ?? "Unknown App";
var userName = _userProviderService.GetCurrentUserId() ?? "Unauthenticated User";
if (entityEntry.State == EntityState.Added)
{
entity.SourceApplication = applicationName;
entity.RowCreationDateTime = DateTimeOffset.UtcNow;
entity.RowCreationUser = userName;
entity.OriginatorIpaddress = string.Join(",", await IpAddressProvider.GetCurrentHostIPv4AddressesAsync());
}
}
}
return result;
}
public InterceptionResult<int> SavingChanges(
DbContextEventData eventData,
InterceptionResult<int> result)
{
return result;
}
}
}
-
Constructor: The interceptor class constructor takes an
IUserProviderService
object, which is used to obtain the current user information.public SaveChangesInterceptor(IUserProviderService userProviderService) { _userProviderService = userProviderService; }
-
SavingChangesAsync Method: This method is called asynchronously when changes are being saved to the database. It processes each entity that implements the
IOriginatorInfo
interface and sets its metadata if it is in theAdded
state.public async ValueTask<InterceptionResult<int>> SavingChangesAsync( DbContextEventData eventData, InterceptionResult<int> result, CancellationToken cancellationToken = default) { var changedEntities = eventData.Context.ChangeTracker.Entries(); foreach (var entityEntry in changedEntities.Where(e => e.Entity is IOriginatorInfo)) { if (entityEntry.Entity is IOriginatorInfo entity) { var connectionString = eventData.Context.Database.GetConnectionString(); SqlConnectionStringBuilder sqlConnectionStringBuilder = new(connectionString); var applicationName = sqlConnectionStringBuilder.ApplicationName ?? "Unknown App"; var userName = _userProviderService.GetCurrentUserId() ?? "Unauthenticated User"; if (entityEntry.State == EntityState.Added) { entity.SourceApplication = applicationName; entity.RowCreationDateTime = DateTimeOffset.UtcNow; entity.RowCreationUser = userName; entity.OriginatorIpaddress = string.Join(",", await IpAddressProvider.GetCurrentHostIPv4AddressesAsync()); } } } return result; }
-
SavingChanges Method: This is a synchronous version of the
SavingChangesAsync
method. It currently doesn't perform any actions and simply returns the result.public InterceptionResult<int> SavingChanges( DbContextEventData eventData, InterceptionResult<int> result) { return result; }
By following these steps, you have created a SaveChangesInterceptor
class that intercepts the process of saving changes to the database and sets metadata on entities implementing the IOriginatorInfo
interface. This metadata can be useful for auditing and tracking purposes.
Fund the project today: Become a GitHub Sponsor
- Home
- Business Ideas
- Who Could Benefit from the "FairPlay" platform?
- Technical Information
- Developers Guides
- FairPlayCombined Applications
- FairPlayDating
- FairPlayTube
- How to setup FairPlayCombined?
- How to deploy FairPlayCombined?
- How to troubleshoot FairPlayCombined?
- Tutorials
- Entrepreneurship Guides
- Improve Your Website SEO
- Upgrading Strategy