See*_*vil 5 entity-framework entity-framework-4.1
我正在编写一个来自网上代码片段的审计线索.在调用我的SaveChanges函数时,我遍历所有使用Context注册的已修改实体,并根据其更改构建日志条目.
foreach (DbEntityEntry modifiedEntity in this.ChangeTracker.Entries().Where(p => p.State == System.Data.EntityState.Added || p.State == System.Data.EntityState.Deleted || p.State == System.Data.EntityState.Modified))
{
// For each changed record, get the audit record entries and add them
foreach(AuditLog x in GetAuditRecordsForChange(modifiedEntity, userId))
{
this.AuditLog.Add(x);
}
}
Run Code Online (Sandbox Code Playgroud)
然后,当我尝试访问修改后的实体的原始值时,将填充所有标量属性,但复杂的属性不存在(属性计数将为6而不是8).然后我调用ToObject()
以原始状态构建对象,但显然复杂属性都是空值.
modifiedEntity.OriginalValues.ToObject()
Run Code Online (Sandbox Code Playgroud)
这只发生在我的一些域对象中,并且这些对象在ToObject()
调用之后总是显示为代理,而(我不确定为什么)但没有实体为它们创建代理的那些,它们的复杂属性填充得很好.当我在我的应用程序中正常使用POCO代理时,延迟加载可以正常工作.
我注意到如果我对其中一个未作为OriginalValues数据的一部分填充的复杂属性进行更改,则对象的状态不会更改为Modified,这是有意义的,因为更改跟踪将原始值与当前值进行比较看它是否改变了.什么没有什么意义的是,该数据仍然坚持在SaveChanged?
编辑:我刚刚注意到,模型对象确实填充了它的复杂属性,所讨论的复杂属性(按惯例)被实体视为"复杂类型",即没有主键.
有任何想法吗?
要获取实体的所有成员名称而不仅仅是您可以使用的简单属性,ObjectContext
而不是DbContext
通过以下方式访问成员列表EntityType
.
((IObjectContextAdapter)this).ObjectContext.ObjectStateManager.GetObjectStateEntry(dbEntry).EntitySet.ElementType.Members
Run Code Online (Sandbox Code Playgroud)
然后,您可以使用方法DbEntityEntry.Member(string propertyName)来获取DbMemberEntry.
获取表示实体成员的对象.返回对象的运行时类型将根据要求的成员类型而有所不同.当前支持的成员类型及其返回类型是引用导航属性(DbReferenceEntry),Collection导航属性(DbCollectionEntry),Primitive /标量属性(DbPropertyEntry)和Complex属性(DbComplexPropertyEntry).
下面的代码示例使用它来记录复杂属性的修改.请注意,在记录复杂的属性更改时,可能会有一些更性感的事情 - 我正在记录整个复杂属性(序列化为JSON),而不仅仅是已更改的内部属性,但它完成了工作.
private IEnumerable<AuditLogEntry> GetAuditLogEntries(DbEntityEntry dbEntry)
{
if (dbEntry.State == EntityState.Added)
{
return new AuditLogEntry { ... };
}
if (dbEntry.State == EntityState.Deleted)
{
return new AuditLogEntry { ... };
}
if (dbEntry.State == EntityState.Modified)
{
// Create one AuditLogEntry per updated field.
var list = new List<AuditLogEntry>();
// We need to object state entry to do deeper things.
ObjectStateEntry objectStateEntry = ((IObjectContextAdapter)this).ObjectContext.ObjectStateManager.GetObjectStateEntry(dbEntry);
// Iterate over the members (i.e. properties (including complex properties), references, collections) of the entity type
foreach (EdmMember member in ((IObjectContextAdapter)this).ObjectContext.ObjectStateManager.GetObjectStateEntry(dbEntry).EntitySet.ElementType.Members)
{
var dbMemberEntry = dbEntry.Member(member.Name) as DbPropertyEntry;
if (dbMemberEntry == null || Equals(dbMemberEntry.OriginalValue, dbMemberEntry.CurrentValue))
{
// Member entry isn't a property entry or it isn't modified.
continue;
}
string oldValue;
string newValue;
if (dbMemberEntry is DbComplexPropertyEntry)
{
// Bit a bit lazy here and just serialise the complex property to JSON rather than detect which inner properties have changed.
var complexProperty = (DbComplexPropertyEntry)dbMemberEntry;
oldValue = EntitySerialiser.Serialise(complexProperty.OriginalValue as IAuditableComplexType);
newValue = EntitySerialiser.Serialise(complexProperty.CurrentValue as IAuditableComplexType);
}
else
{
// It's just a plain property, get the old and new values.
var property = dbMemberEntry;
oldValue = property.OriginalValue.ToStringOrNull();
newValue = property.CurrentValue.ToStringOrNull();
}
list.Add(new AuditLogEntry
{
...,
EventType = AuditEventType.Update,
ColumnName = member.Name,
OriginalValue = oldValue,
NewValue = newValue
});
}
return list;
}
// Otherwise empty.
return Enumerable.Empty<AuditLogEntry>();
}
Run Code Online (Sandbox Code Playgroud)
我期待看到其他解决方案.
我相信这篇文章可以给你一些启发。它不是 EF 4.1,但许多提示和示例都适用。
教程刚过半,该部分的标题就是链接的名称。基本上,要访问复杂类型的原始值,您需要添加一个额外的函数来指定复杂属性。
var original = modifiedEntity.ComplexProperty(u => u.Address).OriginalValues
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
8486 次 |
最近记录: |