DbEntityEntry.OriginalValues不填充复杂属性 [英] DbEntityEntry.OriginalValues not populating complex properties

查看:80
本文介绍了DbEntityEntry.OriginalValues不填充复杂属性的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在从线上的代码片段撰写审计跟踪。在调用我的SaveChanges函数时,我循环遍历所有已注册的Context并修改日志条目。

  foreach(DbEntityEntry modifiedEntity in this.ChangeTracker.Entries()。其中​​(p => p.State == System.Data.EntityState.Added || p.State == System.Data.EntityState.Deleted || p.State == System.Data.EntityState.Modified))
{
//对于每个更改的记录,获取审核记录条目并添加它们
foreach(GetAuditRecordsForChange(modifiedEntity,userId)中的AuditLog x) )
{
this.AuditLog.Add(x);
}
}

当我尝试访问原始值修改的实体,所有的标量属性都被填充,但复杂的属性不存在(属性计数将表示为6而不是8)。然后我调用 ToObject()将对象构建为原始状态,但显然复杂的属性都为空。

  modifiedEntity.OriginalValues.ToObject()

我的域对象的某些,这些对象总是显示为 ToObject()调用后的代理,而(我不知道为什么),但是没有由实体为他们创建的代理,它们的复杂属性填充正确。当我在应用程序中正常使用POCO代理时,懒惰加载工作正常。



我注意到,如果我改变其中之一复杂属性未作为OriginalValues数据的一部分填充,对象的状态不会更改为Modified,因为更改跟踪将原始值与当前值进行比较以查看是否更改,这一点很有道理。什么有意义的是数据仍然保存在SaveChanged?



编辑:我刚刚注意到,模型对象 p>任何想法?

解决方案

要获取实体的所有成员名称,而不仅仅是您可以使用的简单属性 ObjectContext 而不是 DbContext 然后通过 EntityType 。

 ((IObjectContextAdapter)this).ObjectContext.ObjectStateManager.GetObjectStateEntry(dbEntry).EntitySet.ElementType.Members 

然后您可以使用方法 DbEntityEntry.Member(st ring propertyName)获取DbMemberEntry。


获取表示实体成员的对象。返回对象的运行时类型将根据要求的成员类型而有所不同。当前支持的成员类型及其返回类型是参考导航属性(DbReferenceEntry),集合导航属性(DbCollectionEntry),原始/标量属性(DbPropertyEntry)和复杂属性(DbComplexPropertyEntry)。


下面的代码示例使用它来记录复杂属性的修改。注意,在记录复杂的属性更改时,可能会有更多的性能 - 我正在记录整个复杂属性(序列化为JSON),而不仅仅是内部属性已经更改,但它完成了工作。 / p>

  private IEnumerable< AuditLogEntry> GetAuditLogEntries(DbEntityEntry dbEntry)
{
if(dbEntry.State == EntityState.Added)
{
返回新的AuditLogEntry {...};
}

if(dbEntry.State == EntityState.Deleted)
{
返回新的AuditLogEntry {...};
}

if(dbEntry.State == EntityState.Modified)
{
//每更新一个字段创建一个AuditLogEntry。

var list = new List< AuditLogEntry>();

//我们需要对象状态输入来做更深层次的事情。
ObjectStateEntry objectStateEntry =((IObjectContextAdapter)this).ObjectContext.ObjectStateManager.GetObjectStateEntry(dbEntry);

//实例类型为
foreach的成员(即属性(包括复杂属性),引用,集合))迭代((IObjectContextAdapter)中的EdmMember成员)).ObjectContext.ObjectStateManager .BetntStateEntry
if(dbMemberEntry == null || Equals(dbMemberEntry.OriginalValue,dbMemberEntry.CurrentValue))
{
//成员条目不是属性条目,也不会被修改。
继续;
}

string oldValue;
string newValue;

如果(dbMemberEntry是DbComplexPropertyEntry)
{
//这里有点懒惰,只是将复杂属性序列化为JSON,而不是检测哪些内部属性已更改。
var complexProperty =(DbComplexPropertyEntry)dbMemberEntry;
oldValue = EntitySerialiser.Serialise(complexProperty.OriginalValue as IAuditableComplexType);
newValue = EntitySerialiser.Serialise(complexProperty.CurrentValue as IAuditableComplexType);
}
else
{
//这只是一个简单的属性,获取旧的和新的值。
var property = dbMemberEntry;
oldValue = property.OriginalValue.ToStringOrNull();
newValue = property.CurrentValue.ToStringOrNull();
}

list.Add(new AuditLogEntry
{
...,
EventType = AuditEventType.Update,
ColumnName = member。名称,
OriginalValue = oldValue,
NewValue = newValue
});
}

返回列表;
}

//否则为空。
return Enumerable.Empty< AuditLogEntry>();
}

我期待着看到其他解决方案。


I'm writing an audit trail from snippets of code found online. On the call to my SaveChanges function I loop through all the modified entities registered with the Context and build log entries from their changes.

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);
            }
        }

When I then try to access the original values of the modified entity, all the scalar properties are populated but the complex ones don't exist (property count will be say 6 instead of 8). I then call ToObject() to build the object in its original state but obviously the complex properties are all nulls.

modifiedEntity.OriginalValues.ToObject()

This only happens with some of my domain objects, and those objects always show as proxies after the ToObject() call whereas (I'm not sure why) but the ones that don't have proxies created for them by entity, their complex properties populate fine. When I'm using the POCO proxies as normal throughout my application, lazy loading works fine on them.

I've noticed that if I make a change to one of these complex properties that are not populated as part of the OriginalValues data, the object's state doesn't get changed to Modified, this makes sense as change tracking compares the original values to current to see if it's changed. What doesn't make sense is that the data is still persisted on SaveChanged??

EDIT: I've just noticed, the model object that does populate its complex properties, the complex property in question is (by convention) considered a 'complex type' by Entity i.e no primary key.

Any ideas?

解决方案

To get all of the member names of an entity and not just the simple properties you can work with ObjectContext rather than DbContext then access the list of members through the EntityType.

((IObjectContextAdapter)this).ObjectContext.ObjectStateManager.GetObjectStateEntry(dbEntry).EntitySet.ElementType.Members

You can then use the method DbEntityEntry.Member(string propertyName) to get a DbMemberEntry.

Gets an object that represents a member of the entity. The runtime type of the returned object will vary depending on what kind of member is asked for. The currently supported member types and their return types are Reference navigation property (DbReferenceEntry), Collection navigation property (DbCollectionEntry), Primitive/scalar property (DbPropertyEntry) and Complex property (DbComplexPropertyEntry).

The code sample below uses this to log modifications of complex properties. Note that there is probably something sexier to be done when logging complex property changes --- I'm currently logging the whole complex property (serialised to JSON) rather than just the inner properties which have changed, but it gets the job done.

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>();
}

I'm looking forward to seeing other solutions to this.

这篇关于DbEntityEntry.OriginalValues不填充复杂属性的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆