DbEntityEntry.OriginalValues not populating complex properties

假装没事ソ 提交于 2019-12-03 16:11:12

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.

I believe this article may give you some insight. It is not EF 4.1 but many of the tips and examples apply.

Complex Types and the New Change Tracking API

Its a bit before halfway through the tutorial with the title of the section being the name of the link. Basically to access original values with complex type you add an extra function specifying the complex property.

var original = modifiedEntity.ComplexProperty(u => u.Address).OriginalValues

More digging, it seems EF change tracking doesn't store any sort of original values for reference or collection type properties on modified entities (someone please correct me if I'm wrong)

I can find out for example that my Vehicle entity had its reference to one VehicleColour object removed and then re-added pointing to a different instance of a VehicleColour. I can't find out for example that it did point to a VehicleColour with a Name "Stardust Silver" and now points to one with "Azure Blue".

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!