EF Core - set field values automatically

asp.net c# entity-framework entity-framework-core refactoring

Question

I'm developing a web app using ASP.NET Core + Entity Framework Core.

I'd like to store more info regarding creation, modification & deletion.

My EF entities implement interfaces which bring the following fields:

  • DateTime CreationTime { get; set; }
  • long? CreatorUserId { get; set; }
  • DateTime? ModificationTime { get; set; }
  • long? ModifierUserId { get; set; }
  • DateTime? DeletionTime { get; set; }
  • public long? DeleterUserId { get; set; }
  • bool IsDeleted { get; set; }
  • int TenantId { get; set; }

I've been trying to make my app set all of the fields automatically.

Right now I am only able to handle the DateTime fields:

AppDbContext.cs

public override async Task<int> SaveChangesAsync(CancellationToken cancellationToken = default(CancellationToken))
{
    ChangeTracker.DetectChanges();
    ChangeTracker.ProcessModifiedTime();
    ChangeTracker.ProcessSoftDelete();
    ChangeTracker.ProcessCreationTime();

    return (await base.SaveChangesAsync(true, cancellationToken));
}

ChangeTrackerExtensions.cs

public static class ChangeTrackerExtensions
{
    public static void ProcessCreationTime(this ChangeTracker changeTracker)
    {
        foreach (var item in changeTracker.Entries<ICreationTime>().Where(e => e.State == EntityState.Added))
        {
            item.CurrentValues[AppConsts.CreationTime] = DateTime.Now;
        }
    }

    public static void ProcessModifiedTime(this ChangeTracker changeTracker)
    {
        foreach (var item in changeTracker.Entries<IModificationTime>().Where(e => e.State == EntityState.Modified))
        {
            item.CurrentValues[AppConsts.ModificationTime] = DateTime.Now;
        }
    }

    public static void ProcessSoftDelete(this ChangeTracker changeTracker)
    {
        foreach (var item in changeTracker.Entries<ISoftDelete>().Where(e => e.State == EntityState.Deleted))
        {
            item.State = EntityState.Modified;
            item.CurrentValues[AppConsts.IsDeleted] = true;
            item.CurrentValues[AppConsts.DeletionTime] = DateTime.Now;
        }
    }
}

At a Service Layer or Controller level it is possible to assign the required value each time (CreatorUserId, MofidierUserId, DeleterUserId) but this becomes a routine task, very tedious.

I've seen the AspNetBoilerplate implementation, but what worries me about it is that the developers make their DbContext dependent on Session and other stuff.

public abstract class AbpDbContext : DbContext, ITransientDependency, IShouldInitialize
{ 
    public IAbpSession AbpSession { get; set; }
    public IEntityChangeEventHelper EntityChangeEventHelper { get; set; }
    public ILogger Logger { get; set; }
    public IEventBus EventBus { get; set; }
    public IGuidGenerator GuidGenerator { get; set; }
    public ICurrentUnitOfWorkProvider CurrentUnitOfWorkProvider { get; set; }
    public IMultiTenancyConfig MultiTenancyConfig { get; set; }
    ...

IMHO, this is a typical violation of Single Responsibility Principle.

I tried looking into interceptors, but they seem to be available only in ordinary (non-core) edition of Entity Framework - still haven't managed to get my head around how to implement what I mentioned above.

Really, I'm completely lost as to how to handle it with Entity Framework Core. Can anyone think of any Service Layer level solution for that? Or maybe there's a better way?

1
0
9/2/2018 12:10:26 PM

Accepted Answer

UserId and TenantId here are just pieces of data that your repository service (here a DbContext) needs. If a service needs data, it should be injected into the service.

Without DI you might make UserID and TenantID constructor parameters on your DbContext. With DI you might inject a dependency on a service that provides this data.

1
9/1/2018 2:53:16 AM


Related Questions





Related

Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow