Big*_*e10 11 entity entity-framework auditing
我的数据库在每个表上都有一个"LastModifiedUser"列,我打算从进行更改的应用程序中收集登录用户.我不是在谈论数据库用户,所以基本上这只是每个实体上的一个字符串.我想找到一种方法来为每个实体默认这个,以便其他开发人员不必记住在实例化实体时分配它.
所以会发生这样的事情:
using (EntityContext ctx = new EntityContext())
{
MyEntity foo = new MyEntity();
// Trying to avoid having the following line every time
// a new entity is created/added.
foo.LastModifiedUser = Lookupuser();
ctx.Foos.Addobject(foo);
ctx.SaveChanges();
}
Run Code Online (Sandbox Code Playgroud)
Mor*_*avi 25
有一种完美的方法可以通过利用ObjectStateManager
在EF 4.0中实现这一点.
首先,您需要为ObjectContext创建一个分部类并订阅
ObjectContext.SavingChanges事件.订阅此事件的最佳位置是OnContextCreated方法.此方法由上下文对象的构造函数和构造函数重载调用,这是一个没有实现的部分方法:
partial void OnContextCreated() {
this.SavingChanges += Context_SavingChanges;
}
Run Code Online (Sandbox Code Playgroud)
现在将完成这项工作的实际代码:
void Context_SavingChanges(object sender, EventArgs e) {
IEnumerable<ObjectStateEntry> objectStateEntries =
from ose
in this.ObjectStateManager.GetObjectStateEntries(EntityState.Added
| EntityState.Modified)
where ose.Entity != null
select ose;
foreach (ObjectStateEntry entry in objectStateEntries) {
ReadOnlyCollection<FieldMetadata> fieldsMetaData = entry.CurrentValues
.DataRecordInfo.FieldMetadata;
FieldMetadata modifiedField = fieldsMetaData
.Where(f => f.FieldType.Name == "LastModifiedUser").FirstOrDefault();
if (modifiedField.FieldType != null) {
string fieldTypeName = modifiedField.FieldType.TypeUsage.EdmType.Name;
if (fieldTypeName == PrimitiveTypeKind.String.ToString()) {
entry.CurrentValues.SetString(modifiedField.Ordinal, Lookupuser());
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
代码说明:
此代码查找具有LastModifiedUser属性的任何已添加或已修改的条目,然后使用来自自定义Lookupuser()方法的值更新该属性.
在foreach块中,查询基本上钻入每个条目的CurrentValues.然后,使用Where方法,它查看该条目的每个FieldMetaData项的名称,仅选取名称为LastModifiedUser的项.接下来,if语句验证LastModifiedUser属性是否为String
领域; 然后它更新字段的值.
另一种挂钩此方法(而不是订阅SavingChanges事件)的方法是重写ObjectContext.SaveChanges方法.
顺便说一下,上面的代码属于她的编程实体框架书中的Julie Lerman.
编辑自我跟踪POCO实现:
如果您有自我跟踪POCO,那么我要做的是我首先更改T4模板以调用OnContextCreated()方法.如果查看ObjectContext.tt文件,则有一个Initialize()
所有构造函数都调用的方法,因此是调用OnContextCreated()方法的好方法,所以我们需要做的就是更改ObjectContext.tt文件,如下所示:
private void Initialize()
{
// Creating proxies requires the use of the ProxyDataContractResolver and
// may allow lazy loading which can expand the loaded graph during serialization.
ContextOptions.ProxyCreationEnabled = false;
ObjectMaterialized += new ObjectMaterializedEventHandler(HandleObjectMaterialized);
// We call our custom method here:
OnContextCreated();
}
Run Code Online (Sandbox Code Playgroud)
这将导致我们在创建Context时调用OnContextCreated().
现在,如果您将POCO置于服务边界之后,则意味着ModifiedUserName必须包含来自WCF服务使用者的其余数据.您可以将此LastModifiedUser属性公开
给它们以进行更新,或者如果它存储在另一个属性中并且您希望从该属性更新LastModifiedUser,则可以按如下方式修改第二个代码:
foreach (ObjectStateEntry entry in objectStateEntries) {
ReadOnlyCollection fieldsMetaData = entry.CurrentValues
.DataRecordInfo.FieldMetadata;
FieldMetadata sourceField = fieldsMetaData
.Where(f => f.FieldType.Name == "YourPropertyName").FirstOrDefault();
FieldMetadata modifiedField = fieldsMetaData
.Where(f => f.FieldType.Name == "LastModifiedUser").FirstOrDefault();
if (modifiedField.FieldType != null) {
string fieldTypeName = modifiedField.FieldType.TypeUsage.EdmType.Name;
if (fieldTypeName == PrimitiveTypeKind.String.ToString()) {
entry.CurrentValues.SetString(modifiedField.Ordinal,
entry.CurrentValues[sourceField.Ordinal].ToString());
}
}
}
Run Code Online (Sandbox Code Playgroud)
希望这可以帮助.