Jasinski Technical Wiki

Navigation

Home Page
Index
All Pages

Quick Search
»
Advanced Search »

Contributor Links

Create a new Page
Administration
File Management
Login/Logout
Your Profile

Other Wiki Sections

Software

PoweredBy

AuditableBase Class for Automatically Tracking Data Changes - Entity Framework Code First

RSS
Modified on Mon, Nov 30, 2020, 12:27 PM by Administrator Categorized as EF Core, Entity Framework Code First, ┬ĚNet Framework

Reusable Code

AuditableBase Class

public abstract class AuditableBase
{
    [Required]
    public DateTimeOffset CreatedOn { get; set; }

    [Required]
    public DateTimeOffset UpdatedOn { get; set; }

    [Required]
    public int CreatedBy { get; set; }

    [Required]
    public int UpdatedBy { get; set; }

    /* TODO: Uncomment CreatedByUser and UpdatedByUser */
    //[ForeignKey("CreatedBy")]
    //public User CreatedByUser { get; set; }

    //[ForeignKey("UpdatedBy")]
    //public User UpdatedByUser { get; set; }

    public void Audit(int byUserId, bool createNew)
    {
        var dtNow = DateTime.UtcNow;

        if (createNew)
        {
            CreatedOn = dtNow;
            CreatedBy = byUserId;
        }

        UpdatedOn = dtNow;
        UpdatedBy = byUserId;
    }
}

Additional Method on DataContext Class, EF6

#region Auditing

public int SaveChanges(int byUserId)
{
    Audit(byUserId);
    return base.SaveChanges();
}

public async Task<int> SaveChangesAsync(int byUserId)
{
    Audit(byUserId);
    return await base.SaveChangesAsync();
}

public void Audit(int byUserId)
{
    ChangeTracker.DetectChanges(); // normally called by base
    var context = ((IObjectContextAdapter)this).ObjectContext;
    var changes = context.ObjectStateManager.GetObjectStateEntries(EntityState.Added | EntityState.Modified);

    foreach (var entry in changes)
    {
        if (!(entry.Entity is AuditableBase))
            continue;

        var auditable = (AuditableBase)entry.Entity;
        var createNew = (entry.State == EntityState.Added);
        auditable.Audit(byUserId, createNew);
    }
}

#endregion

Additional Method on DataContext Class, EF Core, v1

#region Auditing

public int SaveChanges(int byUserId)
{
    Audit(byUserId);
    return base.SaveChanges();
}

public async Task<int> SaveChangesAsync(int byUserId)
{
    Audit(byUserId);
    return await base.SaveChangesAsync();
}

public void Audit(int byUserId)
{
    var items = this.ChangeTracker.Entries()
        .Where(x => (x.State == EntityState.Added || x.State == EntityState.Modified)
                    && x.Entity != null
                    && x.Entity is AuditableBase)
        .Select(x => x)
        .ToList();

    foreach (var item in items)
    {
        var auditable = (AuditableBase)item.Entity;
        var createNew = (item.State == EntityState.Added);
        auditable.Audit(byUserId, createNew);
    }
}

#endregion

Additional Method on DataContext Class, EF Core, v3

public void Track(string byUsername)
{
    var dtNow = DateTime.UtcNow;

    var items = _changeTracker
        .Entries()
        .Where(x =>
            (x.State == EntityState.Added || x.State == EntityState.Modified)
            && x.Entity != null
            && x.Entity is TrackableBase)
        .Select(x => x)
        .ToList();

    foreach (var item in items)
    {
        var trackable = (TrackableBase)item.Entity;

        if (item.State == EntityState.Added || string.IsNullOrEmpty(trackable.CreatedBy))
        {
            trackable.CreatedBy = byUsername;

            if (trackable.CreatedOn == DateTime.MinValue)
            {
                trackable.CreatedOn = dtNow;
            }
        }

        trackable.UpdatedBy = byUsername;
        trackable.UpdatedOn = dtNow;

                
    }
}

ScrewTurn Wiki version 3.0.1.400. Some of the icons created by FamFamFam. Except where noted, all contents Copyright © 1999-2020, Patrick Jasinski.