/// <summary> /// Creates an audit log entry for a <see cref="DbEntityEntry"/> that has an <see cref="EntityState"/> of <see cref="EntityState.Added"/> or <see cref="EntityState.Modified"/> /// It will create an audit log entry for each property that has changed /// </summary> private static AuditLog CreateAuditLogEntryForAddedOrModified(DatabaseEntities dbContext, DbEntityEntry dbEntry, string tableName, Person person, DateTime changeDate, AuditLogEventType auditLogEventType, DbPropertyEntry modifiedProperty, int tenantID) { var propertyName = modifiedProperty.Name; if (!string.Equals(propertyName, $"{tableName}ID", StringComparison.InvariantCultureIgnoreCase) && !string.Equals(propertyName, "TenantID", StringComparison.InvariantCultureIgnoreCase)) { var optionalAuditDescriptionString = GetAuditDescriptionStringIfAnyForProperty(dbContext, dbEntry, propertyName, auditLogEventType); var auditLogEntry = CreateAuditLogEntryImpl(dbEntry, tableName, person, changeDate, auditLogEventType, propertyName, modifiedProperty.CurrentValue, modifiedProperty.OriginalValue, optionalAuditDescriptionString, tenantID); return(auditLogEntry); } return(null); }
/// <summary> /// Enum types are equal by primary key /// </summary> public bool Equals(AuditLogEventType other) { if (other == null) { return(false); } return(other.AuditLogEventTypeID == AuditLogEventTypeID); }
public string GetAuditDescriptionDisplay() { if (string.IsNullOrWhiteSpace(AuditDescription)) { return(AuditLogEventType.GetAuditStringForOperationType(ColumnName, OriginalValue, NewValue)); } return(AuditDescription); }
/// <summary> /// Constructor for building a new object with MinimalConstructor required fields, using objects whenever possible /// </summary> public AuditLog(Person person, DateTime auditLogDate, AuditLogEventType auditLogEventType, string tableName, int recordID, string columnName, string newValue) : this() { // Mark this as a new object by setting primary key with special value this.AuditLogID = ModelObjectHelpers.MakeNextUnsavedPrimaryKeyValue(); this.PersonID = person.PersonID; this.Person = person; person.AuditLogs.Add(this); this.AuditLogDate = auditLogDate; this.AuditLogEventTypeID = auditLogEventType.AuditLogEventTypeID; this.TableName = tableName; this.RecordID = recordID; this.ColumnName = columnName; this.NewValue = newValue; }
/// <summary> /// Creates an audit log entry for a <see cref="DbEntityEntry"/> that has an <see cref="EntityState"/> of <see cref="EntityState.Deleted"/> /// Deleted log entries do not have columns/property names, so there will just be one record created /// </summary> private static AuditLog CreateAuditLogEntryForDeleted(DbEntityEntry dbEntry, string tableName, Person person, DateTime changeDate, AuditLogEventType auditLogEventType, int tenantID) { var auditableEntityDeleted = GetIAuditableEntityFromEntity(dbEntry.Entity, tableName); var optionalAuditDescriptionString = auditLogEventType.GetAuditStringForOperationType(tableName, null, auditableEntityDeleted.GetAuditDescriptionString()); var auditLogEntry = CreateAuditLogEntryImpl(dbEntry, tableName, person, changeDate, auditLogEventType, "*ALL", AuditLogEventType.Deleted.AuditLogEventTypeDisplayName, null, optionalAuditDescriptionString, tenantID); return(auditLogEntry); }
private static AuditLog CreateAuditLogEntryImpl(DbEntityEntry dbEntry, string tableName, Person person, DateTime changeDate, AuditLogEventType auditLogEventType, string propertyName, object newValue, object originalValue, string optionalAuditDescriptionString, int tenantID) { var recordID = (int)dbEntry.Property(PrimaryKeyName).CurrentValue; var newValueString = newValue != null?newValue.ToString() : string.Empty; var auditLog = new AuditLog(person, changeDate, auditLogEventType, tableName, recordID, propertyName, newValueString, true) { OriginalValue = originalValue?.ToString(), AuditDescription = optionalAuditDescriptionString, TenantID = tenantID }; AddAdditionalRecordColumns(dbEntry, tableName, auditLog); return(auditLog); }
/// <summary> /// TODO: should be able to refactor these convert enum to human readable string functions to one call /// </summary> private static string ConvertProjectLocationAreaEnumToHumanReadableString(ObjectStateEntry objectStateEntry, AuditLogEventType auditLogEventType) { string oldName; var originalValue = objectStateEntry.OriginalValues[PropertyNameProjectLocationAreaID]; var currentValue = objectStateEntry.CurrentValues[PropertyNameProjectLocationAreaID]; if (originalValue is DBNull) { oldName = ""; } else { oldName = string.Empty;// ProjectLocationArea.AllLookupDictionary[((int) originalValue)].ProjectLocationAreaDisplayName; } string newName; if (currentValue is DBNull) { newName = ""; } else { newName = string.Empty; //ProjectLocationArea.AllLookupDictionary[(int) currentValue].ProjectLocationAreaDisplayName; } return(auditLogEventType.GetAuditStringForOperationType("Location", oldName, newName)); }
/// <summary> /// TODO: should be able to refactor these convert enum to human readable string functions to one call /// </summary> private static string ConvertProjectImageTimingEnumToHumanReadableString(ObjectStateEntry objectStateEntry, AuditLogEventType auditLogEventType) { string oldName; var originalValue = objectStateEntry.OriginalValues[PropertyNameProjectImageTimingID]; var currentValue = objectStateEntry.CurrentValues[PropertyNameProjectImageTimingID]; if (originalValue is DBNull) { oldName = ""; } else { var oldPrimaryKeyValue = (int)originalValue; oldName = ProjectImageTiming.AllLookupDictionary[oldPrimaryKeyValue].ProjectImageTimingDisplayName; } var newName = ProjectImageTiming.AllLookupDictionary[(int)currentValue].ProjectImageTimingDisplayName; return(auditLogEventType.GetAuditStringForOperationType("Image Timing", oldName, newName)); }
private static string ConvertEnumsToHumanReadableString(string propertyName, ObjectStateEntry objectStateEntry, AuditLogEventType auditLogEventType) { switch (propertyName) { case PropertyNameProjectStageID: return(ConvertProjectStageEnumToHumanReadableString(objectStateEntry, auditLogEventType)); case PropertyNameProjectImageTimingID: return(ConvertProjectImageTimingEnumToHumanReadableString(objectStateEntry, auditLogEventType)); case PropertyNameProjectLocationAreaID: return(ConvertProjectLocationAreaEnumToHumanReadableString(objectStateEntry, auditLogEventType)); default: // deliberately do nothing for now, expand as needed for other enum return(null); } }
/// <summary> /// Gets the audit description string for a property that came from a <see cref="DbEntityEntry"/> that has an <see cref="EntityState"/> of <see cref="EntityState.Added"/> or <see cref="EntityState.Modified"/> /// This will attempt to look up a foreign key and return a more descriptive string for that fk property /// </summary> public static string GetAuditDescriptionStringIfAnyForProperty(DatabaseEntities dbContext, DbEntityEntry dbEntry, string propertyName, AuditLogEventType auditLogEventType) { var objectContext = dbContext.GetObjectContext(); var objectStateEntry = objectContext.ObjectStateManager.GetObjectStateEntry(dbEntry.Entity); // find foreign key relationships for given propertyname var relatedEnds = GetDependentForeignKeyRelatedEndsForProperty(objectStateEntry, propertyName); foreach (var end in relatedEnds) { var elementType = end.RelationshipSet.ElementType as AssociationType; if (elementType == null || !elementType.IsForeignKey) { continue; } foreach (var constraint in elementType.ReferentialConstraints) { // Multiplicity many means we are looking at a foreign key in a dependent entity // I assume that ToRole will point to a dependent entity, don't know if it can be FromRole Check.Require(constraint.ToRole.RelationshipMultiplicity == RelationshipMultiplicity.Many); // If not 1 then it is a composite key I guess. Becomes a lot more difficult to handle. Check.Require(constraint.ToProperties.Count == 1); var entityName = constraint.FromRole.Name; string auditDescriptionStringForOriginalValue = null; if (!IgnoredTables.Contains(entityName)) { var constraintProperty = constraint.ToProperties[0]; var principalEntity = (EntityReference)end; var newEntityKey = principalEntity.EntityKey; var auditDescriptionStringForNewValue = GetAuditDescriptionStringForEntityKey(objectContext, newEntityKey, entityName); if (newEntityKey != null) { var oldEntityKey = CreateEntityKeyForValue(newEntityKey.EntitySetName, principalEntity.EntityKey.EntityKeyValues[0].Key, objectStateEntry.OriginalValues[constraintProperty.Name]); auditDescriptionStringForOriginalValue = GetAuditDescriptionStringForEntityKey(objectContext, oldEntityKey, entityName); } return(auditLogEventType.GetAuditStringForOperationType(entityName, auditDescriptionStringForOriginalValue, auditDescriptionStringForNewValue)); } } } var enumsToHumanReadableString = ConvertEnumsToHumanReadableString(propertyName, objectStateEntry, auditLogEventType); return(enumsToHumanReadableString); }
/// <summary> /// Creates a "blank" object of this type and populates primitives with defaults /// </summary> public static AuditLog CreateNewBlank(Person person, AuditLogEventType auditLogEventType) { return(new AuditLog(person, default(DateTime), auditLogEventType, default(string), default(int), default(string), default(string))); }