/// <summary> /// Sets the configuration values from attribute, local and global /// </summary> public void SetConfig(IAuditDbContext context) { var type = context.GetType(); if (!_auditAttributeCache.ContainsKey(type)) { _auditAttributeCache[type] = type.GetTypeInfo().GetCustomAttribute(typeof(AuditDbContextAttribute)) as AuditDbContextAttribute; } var attrConfig = _auditAttributeCache[type]?.InternalConfig; var localConfig = Audit.EntityFramework.Configuration.GetConfigForType(type); var globalConfig = Audit.EntityFramework.Configuration.GetConfigForType(typeof(AuditDbContext)); context.Mode = attrConfig?.Mode ?? localConfig?.Mode ?? globalConfig?.Mode ?? AuditOptionMode.OptOut; context.IncludeEntityObjects = attrConfig?.IncludeEntityObjects ?? localConfig?.IncludeEntityObjects ?? globalConfig?.IncludeEntityObjects ?? false; context.ExcludeValidationResults = attrConfig?.ExcludeValidationResults ?? localConfig?.ExcludeValidationResults ?? globalConfig?.ExcludeValidationResults ?? false; context.AuditEventType = attrConfig?.AuditEventType ?? localConfig?.AuditEventType ?? globalConfig?.AuditEventType; context.EntitySettings = MergeEntitySettings(attrConfig?.EntitySettings, localConfig?.EntitySettings, globalConfig?.EntitySettings); context.ExcludeTransactionId = attrConfig?.ExcludeTransactionId ?? localConfig?.ExcludeTransactionId ?? globalConfig?.ExcludeTransactionId ?? false; #if EF_FULL context.IncludeIndependantAssociations = attrConfig?.IncludeIndependantAssociations ?? localConfig?.IncludeIndependantAssociations ?? globalConfig?.IncludeIndependantAssociations ?? false; #endif }
/// <summary> /// Gets the entities changes for an entry entity. /// </summary> /// <param name="entry">The entry.</param> /// <param name="context">The audit database context.</param> private List <EventEntryChange> GetChanges(IAuditDbContext context, DbEntityEntry entry) { var dbContext = context.DbContext; var result = new List <EventEntryChange>(); foreach (var propName in entry.CurrentValues.PropertyNames) { var prop = entry.Property(propName); if (prop.IsModified) { if (IncludeProperty(context, entry, prop.Name)) { var colName = EntityKeyHelper.Instance.GetColumnName(entry.Entity.GetType(), prop.Name, dbContext); var origValue = prop.OriginalValue; var currValue = prop.CurrentValue; result.Add(new EventEntryChange() { ColumnName = colName, NewValue = HasPropertyValue(context, entry, prop.Name, currValue, out object outputNewValue) ? outputNewValue : currValue, OriginalValue = HasPropertyValue(context, entry, prop.Name, origValue, out object outputOriValue) ? outputOriValue : origValue });
/// <summary> /// Creates the Audit Event. /// </summary> public EntityFrameworkEvent CreateAuditEvent(IAuditDbContext context) { var dbContext = context.DbContext; var modifiedEntries = GetModifiedEntries(context); if (modifiedEntries.Count == 0) { return(null); } var dbConnection = IsRelational(dbContext) ? dbContext.Database.GetDbConnection() : null; var clientConnectionId = GetClientConnectionId(dbConnection); var efEvent = new EntityFrameworkEvent() { Entries = new List <EventEntry>(), Database = dbConnection?.Database, ConnectionId = clientConnectionId, TransactionId = GetCurrentTransactionId(dbContext, clientConnectionId) }; foreach (var entry in modifiedEntries) { var entity = entry.Entity; var validationResults = DbContextHelper.GetValidationResults(entity); var entityType = dbContext.Model.FindEntityType(entry.Entity.GetType()); efEvent.Entries.Add(new EventEntry() { Valid = validationResults == null, ValidationResults = validationResults?.Select(x => x.ErrorMessage).ToList(), Entity = context.IncludeEntityObjects ? entity : null, Action = DbContextHelper.GetStateName(entry.State), Changes = entry.State == EntityState.Modified ? GetChanges(dbContext, entry) : null, ColumnValues = GetColumnValues(dbContext, entry), PrimaryKey = GetPrimaryKey(entityType, entity), Table = GetEntityName(entityType) }); } return(efEvent); }
// Determines whether to include the entity on the audit log or not private bool IncludeEntity(IAuditDbContext context, Type type, AuditOptionMode mode) { #if NET45 type = ObjectContext.GetObjectType(type); #endif bool?result = EnsureEntitiesIncludeIgnoreAttrCache(type); //true:excluded false=ignored null=unknown if (result == null) { // No static attributes, check the filters var localConfig = EntityFramework.Configuration.GetConfigForType(context.GetType()); var globalConfig = EntityFramework.Configuration.GetConfigForType(typeof(AuditDbContext)); var included = EvalIncludeFilter(type, localConfig, globalConfig); var ignored = EvalIgnoreFilter(type, localConfig, globalConfig); result = included ? true : ignored ? false : (bool?)null; } if (mode == AuditOptionMode.OptIn) { // Include only explicitly included entities return(result.GetValueOrDefault()); } // Include all, except the explicitly ignored entities return(result == null || result.Value); }
/// <summary> /// Updates column values and primary keys on the Audit Event after the EF save operation completes. /// </summary> public void UpdateAuditEvent(EntityFrameworkEvent efEvent, IAuditDbContext context) { foreach (var efEntry in efEvent.Entries) { var entry = efEntry.Entry; efEntry.PrimaryKey = GetPrimaryKey(context.DbContext, entry); foreach (var pk in efEntry.PrimaryKey) { if (efEntry.ColumnValues.ContainsKey(pk.Key)) { efEntry.ColumnValues[pk.Key] = pk.Value; } } var fks = GetForeignKeys(context.DbContext, entry); foreach (var fk in fks) { if (efEntry.ColumnValues.ContainsKey(fk.Key)) { efEntry.ColumnValues[fk.Key] = fk.Value; } } } }
/// <summary> /// Saves the changes asynchronously. /// </summary> public async Task <int> SaveChangesAsync(IAuditDbContext context, Func <Task <int> > baseSaveChanges) { var dbContext = context.DbContext; if (context.AuditDisabled) { return(await baseSaveChanges()); } var efEvent = CreateAuditEvent(context); if (efEvent == null) { return(await baseSaveChanges()); } var scope = await CreateAuditScopeAsync(context, efEvent); if (context.EarlySavingAudit) { await SaveScopeAsync(context, scope, efEvent); } try { efEvent.Result = await baseSaveChanges(); } catch (Exception ex) { efEvent.Success = false; efEvent.ErrorMessage = ex.GetExceptionInfo(); await SaveScopeAsync(context, scope, efEvent); throw; } efEvent.Success = true; await SaveScopeAsync(context, scope, efEvent); return(efEvent.Result); }
/// <summary> /// Creates the Audit Event. /// </summary> public EntityFrameworkEvent CreateAuditEvent(IAuditDbContext context) { var dbContext = context.DbContext; var modifiedEntries = GetModifiedEntries(context); if (modifiedEntries.Count == 0) { return(null); } var clientConnectionId = GetClientConnectionId(dbContext.Database.Connection); var efEvent = new EntityFrameworkEvent() { Entries = new List <EventEntry>(), Database = dbContext.Database.Connection.Database, ConnectionId = clientConnectionId, TransactionId = GetCurrentTransactionId(dbContext, clientConnectionId), DbContext = dbContext }; foreach (var entry in modifiedEntries) { var entity = entry.Entity; var validationResults = entry.GetValidationResult(); efEvent.Entries.Add(new EventEntry() { Valid = validationResults.IsValid, ValidationResults = validationResults.ValidationErrors.Select(x => x.ErrorMessage).ToList(), Entity = context.IncludeEntityObjects ? entity : null, Entry = entry, Action = GetStateName(entry.State), Changes = entry.State == EntityState.Modified ? GetChanges(dbContext, entry) : null, Table = GetEntityName(dbContext, entity), ColumnValues = GetColumnValues(entry) }); } return(efEvent); }
/// <summary> /// Gets the modified entries to process. /// </summary> #if EF_CORE public List <EntityEntry> GetModifiedEntries(IAuditDbContext context)
public AuditHelper(IAuditDbContext db) { Db = db; }
// Determines whether to include the entity on the audit log or not #if NETCOREAPP1_0 private bool IncludeEntity(IAuditDbContext context, EntityEntry entry, AuditOptionMode mode)
/// <summary> /// Saves the scope. /// </summary> public void SaveScope(IAuditDbContext context, AuditScope scope, EntityFrameworkEvent @event) { (scope.Event as AuditEventEntityFramework).EntityFrameworkEvent = @event; context.OnScopeSaving(scope); scope.Save(); }
public DepartmentContext2() { auditDbContext = new DefaultAuditContext(this); auditDbContext.IncludeEntityObjects = true; helper.SetConfig(auditDbContext); }