EF审计创作
EF Auditing Creations
好的,这里发生了很多事情,我不想用冗长的代码示例让你们感到厌烦,所以这里是摘录...
当在我的 EF 上下文中调用 SaveChangesAsync() 时,我让它调用此方法来审核每个条目...
async Task Audit(DbEntityEntry<IAmAuditable> entry)
{
try
{
var newAuditEntry = new AuditEntry
{
EntityType = entry.Entity.GetType().Name,
Event = entry.State.ToString(),
SSOUserId = kernel.Get<User>().Id,
EntityId = entry.GetId().ToString(),
EventId = eventId
};
如果有问题的条目是一个实体创建,这将导致在数据库上插入,那么我也会这样做...
var properties = entry.CurrentValues.PropertyNames.Select(p => entry.Property(p)).ToList();
var addedValues = new List<AuditDataItem>();
foreach (var p in properties)
{
addedValues.Add(new AuditDataItem
{
PropertyName = p.Name,
PreviousValue = null,
NewValue = p.CurrentValue.ToString()
});
}
newAuditEntry.Changes = addedValues;
break;
...这是它失败的地方...在那个时间点,对 SaveChanges 的基本调用尚未执行,因此所讨论的实体还没有主键值...最终结果是我记录了一个没有主要实体的创建。
有没有人对处理此问题的简洁方法有任何建议,以便我可以将新的主键值放入 AuditDataItem 中?
编辑:
这是我目前记录的示例 json,这是单个 AuditEntry 对象和部分 AuditDataItem 子行的一部分...
{
"Id": 4,
"SSOUserId": 1,
"EventId": "6d862aad-0898-4794-aea0-00af6f2994ff",
"EntityType": "AC_Programme",
"Event": "Added",
"TimeOfEvent": "2016-02-04T12:04:31.5501508+01:00",
"Changes": [
{
"Id": 34,
"PropertyName": "Id",
"PreviousValue": null,
"NewValue": "0"
},
{
"Id": 35,
"PropertyName": "Name",
"PreviousValue": null,
"NewValue": "Test"
},
...
]
}
据我所知,在创建对象后,没有任何事件可以拦截对象创建。有以下活动:
- ObjectContext.SavingChanges:将更改保存到数据源时发生。
- ObjectContext.ObjectMaterialized:在作为查询或加载操作的一部分从数据源中的数据创建新实体对象时发生。
第一次发生在保存更改之前(与您遇到的问题相同)。第二种情况发生在作为查询结果或 .Load
从数据库中读取实体时,因此它不适合您的情况。
我能想到的唯一解决办法就是重写原来的SaveChanges,在重写的方法中进行:
- 查找处于已添加状态的所有实体,并保留对它们的引用,例如将它们添加到
List<Object>
- 调用基础保存更改,以便在数据库中具体化更改,并更新 DbCOntext 中的实体
- 访问列表中的实体,您将获得所有数据库生成的属性(如计算、身份、guid 等),以便您可以正确记录它们
Logging and Intercepting Database Operations (EF6 Onwards) 不符合您的需求
您需要将 ObjectStateEntry 保留在 AuditEntry 中,然后在 PostSaveChanges 事件中重新访问主键为 "Temporary" 的每个 AuditEntry。
这是一个例子:
显然,我建议您使用 EF+ Audit 而不是创建自己的库,但如果您仍想对其进行编码,该库是开源的,因此您可以找到很多信息来帮助您。
免责声明:我是项目的所有者EF+ (EntityFramework Plus)
好的,这就是我想出的...很想知道你们的想法...
public override async Task<int> SaveChangesAsync()
{
try
{
await AuditChanges(new[] { EntityState.Modified, EntityState.Deleted });
var result = await base.SaveChangesAsync();
await AuditChanges(new[] { EntityState.Added });
return result;
}
catch (DbEntityValidationException ex) { throw ConstructDetailsFor(ex); }
}
async Task AuditChanges(EntityState[] states)
{
var auditableEntities = ChangeTracker.Entries<IAmAuditable>()
.Where(e => states.Contains(e.State));
foreach (var entry in auditableEntities)
await Audit(entry);
}
async Task Audit(DbEntityEntry<IAmAuditable> entry)
{
...
这很简单:)
然后我的审计方法基本上进入一个 switch 语句,并根据从更改跟踪器传入的可审计实体条目来决定 运行 的逻辑。
我认为审计不会比这简单得多。
我将它放入通用基础 class 用于我所有的 EF 上下文和 运行 迁移以将其应用于所有数据库和 bang ... 到处都是动态的,所有的自动审计用 "IAmAuditable" 标记的实体(空标记界面)。
我考虑过使用一个属性,但这需要反射,什么不需要。
好的,这里发生了很多事情,我不想用冗长的代码示例让你们感到厌烦,所以这里是摘录...
当在我的 EF 上下文中调用 SaveChangesAsync() 时,我让它调用此方法来审核每个条目...
async Task Audit(DbEntityEntry<IAmAuditable> entry)
{
try
{
var newAuditEntry = new AuditEntry
{
EntityType = entry.Entity.GetType().Name,
Event = entry.State.ToString(),
SSOUserId = kernel.Get<User>().Id,
EntityId = entry.GetId().ToString(),
EventId = eventId
};
如果有问题的条目是一个实体创建,这将导致在数据库上插入,那么我也会这样做...
var properties = entry.CurrentValues.PropertyNames.Select(p => entry.Property(p)).ToList();
var addedValues = new List<AuditDataItem>();
foreach (var p in properties)
{
addedValues.Add(new AuditDataItem
{
PropertyName = p.Name,
PreviousValue = null,
NewValue = p.CurrentValue.ToString()
});
}
newAuditEntry.Changes = addedValues;
break;
...这是它失败的地方...在那个时间点,对 SaveChanges 的基本调用尚未执行,因此所讨论的实体还没有主键值...最终结果是我记录了一个没有主要实体的创建。
有没有人对处理此问题的简洁方法有任何建议,以便我可以将新的主键值放入 AuditDataItem 中?
编辑:
这是我目前记录的示例 json,这是单个 AuditEntry 对象和部分 AuditDataItem 子行的一部分...
{
"Id": 4,
"SSOUserId": 1,
"EventId": "6d862aad-0898-4794-aea0-00af6f2994ff",
"EntityType": "AC_Programme",
"Event": "Added",
"TimeOfEvent": "2016-02-04T12:04:31.5501508+01:00",
"Changes": [
{
"Id": 34,
"PropertyName": "Id",
"PreviousValue": null,
"NewValue": "0"
},
{
"Id": 35,
"PropertyName": "Name",
"PreviousValue": null,
"NewValue": "Test"
},
...
]
}
据我所知,在创建对象后,没有任何事件可以拦截对象创建。有以下活动:
- ObjectContext.SavingChanges:将更改保存到数据源时发生。
- ObjectContext.ObjectMaterialized:在作为查询或加载操作的一部分从数据源中的数据创建新实体对象时发生。
第一次发生在保存更改之前(与您遇到的问题相同)。第二种情况发生在作为查询结果或 .Load
从数据库中读取实体时,因此它不适合您的情况。
我能想到的唯一解决办法就是重写原来的SaveChanges,在重写的方法中进行:
- 查找处于已添加状态的所有实体,并保留对它们的引用,例如将它们添加到
List<Object>
- 调用基础保存更改,以便在数据库中具体化更改,并更新 DbCOntext 中的实体
- 访问列表中的实体,您将获得所有数据库生成的属性(如计算、身份、guid 等),以便您可以正确记录它们
Logging and Intercepting Database Operations (EF6 Onwards) 不符合您的需求
您需要将 ObjectStateEntry 保留在 AuditEntry 中,然后在 PostSaveChanges 事件中重新访问主键为 "Temporary" 的每个 AuditEntry。
这是一个例子:
显然,我建议您使用 EF+ Audit 而不是创建自己的库,但如果您仍想对其进行编码,该库是开源的,因此您可以找到很多信息来帮助您。
免责声明:我是项目的所有者EF+ (EntityFramework Plus)
好的,这就是我想出的...很想知道你们的想法...
public override async Task<int> SaveChangesAsync()
{
try
{
await AuditChanges(new[] { EntityState.Modified, EntityState.Deleted });
var result = await base.SaveChangesAsync();
await AuditChanges(new[] { EntityState.Added });
return result;
}
catch (DbEntityValidationException ex) { throw ConstructDetailsFor(ex); }
}
async Task AuditChanges(EntityState[] states)
{
var auditableEntities = ChangeTracker.Entries<IAmAuditable>()
.Where(e => states.Contains(e.State));
foreach (var entry in auditableEntities)
await Audit(entry);
}
async Task Audit(DbEntityEntry<IAmAuditable> entry)
{
...
这很简单:)
然后我的审计方法基本上进入一个 switch 语句,并根据从更改跟踪器传入的可审计实体条目来决定 运行 的逻辑。
我认为审计不会比这简单得多。
我将它放入通用基础 class 用于我所有的 EF 上下文和 运行 迁移以将其应用于所有数据库和 bang ... 到处都是动态的,所有的自动审计用 "IAmAuditable" 标记的实体(空标记界面)。
我考虑过使用一个属性,但这需要反射,什么不需要。