Skip to content

Instantly share code, notes, and snippets.

@garima2510
Last active September 16, 2020 04:28
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save garima2510/3da164fd96a8cab0e16e04fb79a08bd4 to your computer and use it in GitHub Desktop.
Save garima2510/3da164fd96a8cab0e16e04fb79a08bd4 to your computer and use it in GitHub Desktop.
Partial class to override SaveChanges of DbContext and use it for creating audit trail
public partial class CustomDbContext: DbContext
{
public override int SaveChanges()
{
// Get all Added/Deleted/Modified entities (not Unmodified or Detached)
//you can get updated and deleted in same collection. I am doing so for ease of purpose
//getting added entries in different collection to fetch their generated ids and then log them
var addedEntries = ChangeTracker.Entries().Where(e => e.State == EntityState.Added).ToList();
var updatedEntries = ChangeTracker.Entries().Where(e => e.State == EntityState.Modified).ToList();
var deletedEntries = ChangeTracker.Entries().Where(e => e.State == EntityState.Deleted).ToList();
foreach (var entry in updatedEntries)
{
ApplyAuditLog(entry);
}
foreach (var entry in deletedEntries)
{
ApplyAuditLog(entry);
}
int changes = base.SaveChanges();
foreach (var entry in addedEntries)
{
ApplyAuditLog(entry);
}
base.SaveChanges();
return changes;
}
/// <summary>
/// code to enter values in audit table
/// </summary>
/// <param name="entry"></param>
/// <param name="auditType"></param>
private void ApplyAuditLog(DbEntityEntry entry)
{
//get table name of entry
string tableName = GetTableName(entry);
//exlude audit table from getting audited
if (!tableName.Equals("Audit", StringComparison.OrdinalIgnoreCase))
{
//code to get primary key (id) of object which is updated
var objectStateEntry = ((IObjectContextAdapter)this).ObjectContext.ObjectStateManager.GetObjectStateEntry(entry.Entity);
int primaryKeyOfEntity = (int)objectStateEntry.EntityKey.EntityKeyValues[0].Value;
DateTime currentDateTime = DateTime.Now;
//code to convert the updated/added/deleted entry to json using newtonsoft json nuget package
string serializedJson = JsonConvert.SerializeObject(entry.CurrentValues.ToObject(), Formatting.Indented, new JsonSerializerSettings
{
//below code ignores the circular references. Main reason XML is not used as this was so wasy in JSON
ReferenceLoopHandling = ReferenceLoopHandling.Ignore
});
Audit auditTrail = new Audit()
{
TimeStamp = currentDateTime,
AuthenticatedUser = {/* code to log authenticated user */ },
LinkedID = primaryKeyOfEntity,
AuditType = {/*create/update/delete/or read. Write your own logic here */ },
TableName = tableName,
NewValue = serializedJson
};
//you can write code here to generated checksum using algo like SHA1, MD5 and then update it in audit trail
//string checksumValue = GetChecksumValueFromTrail(auditTrail);
//write code to add entry in audit table here like below
//Audit.Add(auditTrail);
}
}
/// <summary>
/// method to fetch table name of updated entity
/// </summary>
/// <param name="dbEntityEntry"></param>
/// <returns></returns>
private string GetTableName(DbEntityEntry dbEntityEntry)
{
ObjectContext objectContext = ((IObjectContextAdapter)this).ObjectContext;
Type entityType = dbEntityEntry.Entity.GetType();
if (entityType.BaseType != null && entityType.Namespace == "System.Data.Entity.DynamicProxies")
entityType = entityType.BaseType;
string entityTypeName = entityType.Name;
EntityContainer container =
objectContext.MetadataWorkspace.GetEntityContainer(objectContext.DefaultContainerName, DataSpace.CSpace);
string entitySetName = (from meta in container.BaseEntitySets
where meta.ElementType.Name == entityTypeName
select meta.Name).First();
return entitySetName;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment