DbEntityEntry.OriginalValues ​​не заполняет сложные свойства - PullRequest
5 голосов
/ 11 августа 2011

Я пишу контрольный журнал из фрагментов кода, найденного в Интернете. При вызове моей функции 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);
            }
        }

Когда я затем пытаюсь получить доступ к исходным значениям модифицированного объекта, все скалярные свойства заполняются, но сложные свойства не существуют (количество свойств будет, скажем, 6 вместо 8). Затем я вызываю ToObject(), чтобы построить объект в его первоначальном состоянии, но очевидно, что все сложные свойства имеют значение null.

modifiedEntity.OriginalValues.ToObject()

Это происходит только с некоторыми объектами моего домена, и эти объекты всегда отображаются как прокси после вызова ToObject(), тогда как (я не уверен почему), но те, у которых нет прокси созданные для них сущности, их сложные свойства заполняются прекрасно. Когда я использую прокси-серверы POCO, как обычно, во всех приложениях, отложенная загрузка работает нормально.

Я заметил, что если я внесу изменение в одно из этих сложных свойств, которые не заполняются как часть данных OriginalValues, состояние объекта не изменится на Изменено, это имеет смысл, так как отслеживание изменений сравнивает оригинал значения к текущему, чтобы увидеть, если он изменился. Что не имеет смысл, так это то, что данные все еще сохраняются в SaveChanged ??

РЕДАКТИРОВАТЬ: Я только что заметил, объект модели, который делает заполняет свои сложные свойства, рассматриваемое комплексное свойство (по соглашению) рассматривается Entity как «сложный тип», т.е.

Есть идеи?

Ответы [ 3 ]

8 голосов
/ 10 ноября 2011

Чтобы получить все имена членов сущности, а не только простые свойства, с которыми вы можете работать ObjectContext вместо DbContext, получите доступ к списку членов через EntityType.

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

Затем вы можете использовать метод DbEntityEntry.Member (string propertyName) , чтобы получить DbMemberEntry.

Получает объект, который представляет член сущности. Тип времени выполнения возвращаемого объекта будет варьироваться в зависимости от того, какой тип элемента запрашивается. В настоящее время поддерживаются следующие типы элементов и их возвращаемые типы: свойство навигации по ссылкам (DbReferenceEntry), свойство навигации по коллекциям (DbCollectionEntry), свойство примитива / скаляра (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>();
}

Я с нетерпением жду возможности увидеть другие решения этой проблемы.

4 голосов
/ 12 августа 2011

Я полагаю, что эта статья может дать вам некоторое представление.Это не EF 4.1, но применимы многие советы и примеры.

Сложные типы и новый API отслеживания изменений

Это немного раньше, чем на полпути учебника сназвание раздела, являющегося названием ссылки.В основном для доступа к исходным значениям со сложным типом вы добавляете дополнительную функцию, указывающую комплексное свойство.

var original = modifiedEntity.ComplexProperty(u => u.Address).OriginalValues
1 голос
/ 12 августа 2011

Больше копаний, похоже, EF отслеживание изменений не хранит какие-либо оригинальные значения для свойств ссылки или типа коллекции на измененных объектах (кто-то, пожалуйста, исправьте меня, если я ошибаюсь)

Я могу узнатьнапример, у моей сущности Vehicle была удалена ссылка на один объект VehicleColour, а затем повторно добавлена ​​ссылка на другой экземпляр VehicleColour.Я не могу найти, например, что он действительно указывал на VehicleColour с именем "Stardust Silver" и теперь указывает на один с "Azure Blue".

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...