【发布时间】:2014-10-30 22:43:08
【问题描述】:
出于审计日志记录的目的,我需要获取所有列的值,包括已为数据库中的一个表修改的 FK 实体和关系实体。数据库基本上是用于用户可以上传资源(文件、在线文档、图片等)的网站,我有一个名为 Material 的表,它有多个多 2 多和一对二一关系,如 Material - Audience 、Material - Category、'Material-Uploader'、'Material-PermissionMaterial -Tags 等。我想记录发生在Material 上的所有更改。例如,如果有人从材料中删除了标签,那么我需要记录:
- [User12 - 12/12/12] -
Happy标签已从Crappy材料中移除。
到目前为止,我得到了这个:我可以得到所有被修改、添加、删除的ObjectStateEntries:
context.ObjectStateManager.GetObjectStateEntries(EntityState.Added | EntityState.Deleted | EntityState.Modified)
现在,我可以使用以下方法检查此 ObjectStateEntry 是否为 RelationShip:
if (e.IsRelationship) {
HandleRelationshipEntry(e);
}
else {
HandleEntry(e);
}
在HandleEntry方法中(入口不是关系入口),我可以检查Entry的类型,在我的例子中是Material,所以我在做:
// We care about only Material which are modifed
if (e.State != EntityState.Modified || !(e.Entity is Material))
return;
有一次,我知道Entry 是Material Entry 类型,我可以使用以下方法获取Material 表中所有已更改的列:
e.CurrentValues[ARCHIVE_COLUMN].ToString() != e.OriginalValues[ARCHIVE_COLUMN].ToString()
此时,我可以记录Material 表的所有非FK 更改。但是如果列是FK 到其他一些entity,我无法将FK 值解析为对应的Entity。我只能知道CategoryID 已从42 更改为76,但我无法解析Category 本身的名称。我尝试了将DBDataRecord 和CurrentValueRecord 转换为EntityKey 的方法,但它只是NULL。有没有办法使用ObjectStateManager解决这些FKs到Entities?
我的完整代码供参考:
私有类 SingleMaterialLogger { MaterialAuditData auditData = new MaterialAuditData(); 公共无效HandleEntity(ObjectStateEntry e,ObjectContext上下文){ HandlePrimaryTypeChanges(e); HandleComplexTypeChanges(e, context); }
private void HandleComplexTypeChanges(ObjectStateEntry e, ObjectContext c) {
// Owner, Category, Contact
ChangeValueHelper(e, CONTACT_COLUMN, (k1, k2) => {
// get old value
User old = c.GetObjectByKey(k1) as User;
User current = c.GetObjectByKey(k2) as User;
});
}
public void HandlePrimaryTypeChanges(ObjectStateEntry e) {
// Name, Description, ArchiveDate, Status
// Again no reflection is used - So change them if column name changes
ChangeValueHelper<string>(e, NAME_COLUMN, (change) => auditData.Name = change);
ChangeValueHelper<string>(e, NAME_COLUMN, (change) => auditData.Description = change);
// TODO - Fix change value helper
if (e.CurrentValues[ARCHIVE_COLUMN].ToString() != e.OriginalValues[ARCHIVE_COLUMN].ToString()) {
auditData.ArchiveDate = new Change<DateTime?>(e.OriginalValues[ARCHIVE_COLUMN] as DateTime?, e.CurrentValues[ARCHIVE_COLUMN] as DateTime?);
}
}
private void ChangeValueHelper(ObjectStateEntry e, string columnName, Action<EntityKey, EntityKey> func) {
if (e.CurrentValues[columnName].ToString() != e.OriginalValues[columnName].ToString()) {
func(e.OriginalValues[columnName] as EntityKey, e.CurrentValues[columnName] as EntityKey);
}
}
private void ChangeValueHelper<T>(ObjectStateEntry e, string columnName, Action<Change<T>> func) where T : class {
if(e.CurrentValues[columnName].ToString() != e.OriginalValues[columnName].ToString()) {
func(new Change<T>(e.OriginalValues[columnName] as T, e.OriginalValues[columnName] as T));
}
}
}
Dictionary<EntityKey, SingleMaterialLogger> singleMaterialLoggerMap = new Dictionary<EntityKey, SingleMaterialLogger>();
private ObjectContext context;
public MaterialAuditLogger(ObjectContext context) {
this.context = context;
}
public void AuditMaterialChanges() {
// Grab everything thats being added/deleted/modified
foreach(var e in context.ObjectStateManager.GetObjectStateEntries(EntityState.Added | EntityState.Deleted | EntityState.Modified)) {
if (e.IsRelationship) {
HandleRelationshipEntity(e);
}
else {
HandleEntity(e);
}
}
}
private void HandleEntity(ObjectStateEntry e) {
// We care about only Material which are modifed
if (e.State != EntityState.Modified || !(e.Entity is Material))
return;
var logger = SingleLogger(e.EntityKey);
logger.HandleEntity(e, context);
}
private void HandleRelationshipEntity(ObjectStateEntry e) {
// relations whose entity keys contains
}
private SingleMaterialLogger SingleLogger(EntityKey key) {
if(singleMaterialLoggerMap.ContainsKey(key))
return singleMaterialLoggerMap[key];
SingleMaterialLogger logger = new SingleMaterialLogger();
singleMaterialLoggerMap[key] = logger;
return logger;
}
【问题讨论】:
标签: c# database entity-framework