Shallow Auditing - adamfoneil/Dapper.Repository GitHub Wiki

I use the term "shallow auditing" to mean capturing the user name and timestamp on inserted and updated rows, without tracking prior row states. Since you'll need to make some design decisions as to how exactly you want to capture this kind of information, this is not "built-into" the Dapper.Repository package. But this topic describes a sample approach that should give you ideas on how to implement in your own applications.

  1. Decide on what user and timestamp info you want to capture on all your entity classes, and create a BaseTable or BaseEntity class. All entity classes in your application should then derive from this. It should likely be abstract so you aren't tempted to create it as a standalone table. It should also implement IModel as that is a Dapper.Repository requirement. In this example, I have CreatedBy and ModifiedBy columns along with their associated timestamp columns. I use the [SaveAction] attribute to indicate when those respective columns are updated.
Source
public abstract class BaseTable : IModel<int>
{
    public int Id { get; set; }

    [SaveAction(SaveAction.Insert)]
    [MaxLength(50)]
    [Required]
    public string CreatedBy { get; set; }

    [SaveAction(SaveAction.Insert)]
    public DateTime DateCreated { get; set; }

    [MaxLength(50)]
    [SaveAction(SaveAction.Update)]
    public string ModifiedBy { get; set; }

    [SaveAction(SaveAction.Update)]
    public DateTime? DateModified { get; set; }
}

  1. Create or modify the TUser class used with your DbContext class as described in the home page topic Working with TUser. This example creates a class UserInfo which relies on Timestamp.Local to get the user's local time based on their stated time zone.
Source
public class UserInfo
{
    public string UserName { get; set; } 
    public string TimeZoneId { get; set; }
    public DateTime LocalTime => Timestamp.Local(TimeZoneId);
}

  1. Create a base Repository class that all your subsequent repositories will derive from in your application, and override the BeforeSaveAsync method as shown in the suggestion.
Source
public class BaseRepository<TModel> : Repository<UserInfoResult, TModel, int> where TModel : IModel<int>
{
    public BaseRepository(DataContext context) : base(context)
    {
    }

    protected override async Task BeforeSaveAsync(IDbConnection connection, SaveAction action, TModel model, IDbTransaction txn = null)
    {
        if (model is BaseTable baseTable)
        {
            switch (action)
            {
                case SaveAction.Insert:
                    baseTable.CreatedBy = Context.User.UserName;
                    baseTable.DateCreated = Context.User.LocalTime;
                    break;

                case SaveAction.Update:
                    baseTable.ModifiedBy = Context.User.UserName;
                    baseTable.DateModified = Context.User.LocalTime;
                    break;
            }
        }

        await Task.CompletedTask;
    }
}
  1. Create additional Repository classes for your application as needed, all deriving from BaseRepository. This way, all your CRUD operations across your application will capture "shallow auditing" consistently.
⚠️ **GitHub.com Fallback** ⚠️