-
-
Save leniency/76f5ea2195adad162d56 to your computer and use it in GitHub Desktop.
AuxDbContext
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/// <summary> | |
/// Delegate definition for when the database context has been successfully saved. | |
/// </summary> | |
public delegate void OnContextSaved(); | |
/// <summary> | |
/// Database context extensions. Provides additional functionality. | |
/// </summary> | |
/// <remarks> | |
/// <list type="1"> | |
/// <listheader>Additions</listheader> | |
/// <item>Extra <see cref="System.Data.Entity.Validation.DbEntityValidationException"/> message descriptions.</item> | |
/// <item>OnSaved event handler. Allows hooks into a successful save.</item> | |
/// <item>Audit history. Provides a storage model for audit tracking.</item> | |
/// </list> | |
/// </remarks> | |
public abstract class AuxDbContext : DbContext | |
{ | |
/// <summary> | |
/// OnSaved Event - called after a successful SaveChanges() | |
/// </summary> | |
public event OnContextSaved OnSaved; | |
/// <summary> | |
/// Initialize a new instance of Aux.Data.AuxDbContext | |
/// </summary> | |
/// <param name="connection">The connection string.</param> | |
public AuxDbContext(string connection) | |
: base(connection) | |
{ | |
// Add an additional OnSaving Event. | |
// This checks any modified entities to see if they have the | |
// [Audit] attribute on them for history tracking. | |
((IObjectContextAdapter)this).ObjectContext.SavingChanges += OnSaving; | |
} | |
/// <summary> | |
/// Saves all changes made in this context to the underlying database. | |
/// </summary> | |
/// <returns>The number of objects written to the underlying database.</returns> | |
public override int SaveChanges() | |
{ | |
int result; | |
try | |
{ | |
result = base.SaveChanges(); | |
} | |
catch (DbEntityValidationException ex) | |
{ | |
// Catch any validation issues and reformat their error. | |
// The default error gives nothing after the fact so attempt to give | |
// some more meaningful information in the exceptoin message. | |
var messages = ex.EntityValidationErrors | |
.Where(e => !e.IsValid) | |
.Select(errors => errors | |
.ValidationErrors.ToString(", ", e => " { " + errors.Entry.Entity.GetType().Name + "." + e.PropertyName + ": " + e.ErrorMessage + " } ")); | |
throw new DbEntityValidationException("Validation failed for one or more entities: " + messages.ToString("\r\n"), ex); | |
} | |
if (OnSaved != null) | |
{ | |
OnSaved(); | |
} | |
return result; | |
} | |
/// <summary> | |
/// Check for audit tracking while saving a record | |
/// </summary> | |
/// <param name="sender"></param> | |
/// <param name="args"></param> | |
private void OnSaving(object sender, EventArgs args) | |
{ | |
// Only look at entities that were marked as modified | |
foreach (var entry in this.ChangeTracker.Entries().Where(e => e.State == System.Data.EntityState.Modified)) | |
{ | |
var properties = entry.Entity.GetType().GetProperties(); | |
// Check each entity property for things with the Audit attribute. | |
foreach (var property in properties) | |
{ | |
var audit = property.GetCustomAttributes(typeof(AuditAttribute), true); | |
// Has a single audit property (multiple not allowed, nor does it make sense) | |
if (audit != null && audit.Length == 1) | |
{ | |
var member = entry.Member(property.Name); | |
// Get the current value. | |
string current = (member.CurrentValue != null) | |
? member.CurrentValue.ToString() | |
: null; | |
string original = String.Empty; | |
// Simple property or complex type | |
if (member is DbPropertyEntry) | |
{ | |
original = ((member as DbPropertyEntry).OriginalValue != null) | |
? (member as DbPropertyEntry).OriginalValue.ToString() | |
: null; | |
// Check if it really changed, sometimes | |
// (member as DbPropertyEntry).IsModified throws false positive. | |
if (original == current) | |
{ | |
continue; // no change | |
} | |
} | |
else if (member is DbReferenceEntry) | |
{ | |
// Original values not available for references | |
original = "{ Reference Type - Original not available. }"; | |
} | |
var h = new History | |
{ | |
Changed = DateTime.Now, | |
User = Thread.CurrentPrincipal.Identity.Name, | |
OriginalValue = original, | |
NewValue = current, | |
Column = member.Name, | |
Table = (this.IsProxy(entry.Entity) | |
? entry.Entity.GetType().BaseType | |
: entry.Entity.GetType()) | |
.FullName, | |
EntityId = History.FormatEntityKey(this.GetEntityKey(entry.Entity).EntityKeyValues.ToDictionary(k => k.Key, v => v.Value)) | |
}; | |
// Ensure that we have something for the user name | |
if (String.IsNullOrWhiteSpace(h.User)) | |
{ | |
h.User = "{ Anonymous.User }"; | |
} | |
Set<History>().Add(h); | |
} | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment