Entity Framework 6 - DataServiceContext Detect имеет изменения

У меня есть приложение сервера WCF под управлением Entity Framework 6.

Мое клиентское приложение использует OData с сервера через DataServiceContext, и в моем клиентском коде я хочу иметь возможность вызывать метод HasChanges() для контекста, чтобы увидеть, изменились ли какие-либо данные в нем.

Я попытался использовать следующий метод расширения:

    public static bool HasChanges(this  DataServiceContext ctx)
    {
        // Return true if any Entities or links have changes
        return ctx.Entities.Any(ed => ed.State != EntityStates.Unchanged) || ctx.Links.Any(ld => ld.State != EntityStates.Unchanged);
    }

Но он всегда возвращает false, даже если отслеживаемый объект действительно имеет изменения.

Например, учитывая, что у меня есть отслеживаемая сущность с именем Customer, следующий код всегда возвращается перед вызовом SaveChanges().

    Customer.Address1 = "Fred"
    if not ctx.HasChanges() then return
    ctx.UpdateObject(Customer)
    ctx.SaveChanges()

Если я закомментирую if, а не ctx.HasChanges(), а затем верну строку кода, изменения будут успешно сохранены, поэтому я рад, что объект получил изменение и может его сохранить.

Кажется, что изменение отслеживается контекстом, просто я не могу определить этот факт из своего кода.

Может кто-нибудь сказать мне, как определить HasChanges на DataServiceContext?

4 ответа

Решение

Далеко Я просто прочитал DataServiceContext.UpdateObjectInternal(entity, failIfNotUnchanged), который вызывается прямо из UpdateObject(entity) с false аргумент.

Логика выглядит так:

  • Если уже изменено, вернуть; (короткое замыкание)
  • Если не без изменений, киньте если failIfNotUnchanged; (верно только с ChangeState())
  • В противном случае установите состояние на измененное. (проверка данных не проводилась)

Итак, судя по всему, UpdateObject не заботится / не проверяет внутреннее состояние объекта, а только State ENUM. Это делает обновления немного неточными, когда нет изменений.

Тем не менее, я думаю, что ваша проблема заключается во втором блоке кода OP, вы проверяете свое расширение HasChanges перед звонком UpdateObject, Субъектами являются только прославленные POCO (как вы можете прочитать в Reference.cs (Показать скрытые файлы, затем в разделе "Справочник по обслуживанию"). У них есть очевидные свойства и несколько On- операции по уведомлению об изменении. Что они не делают внутренне, так это состояние трассы. На самом деле, есть EntityDescriptor связан с объектом, который отвечает за отслеживание состояния в EntityTracker.TryGetEntityDescriptor(entity),

Суть в том, что операции на самом деле работают очень просто, и я думаю, что вам просто нужно, чтобы ваш код был похож

Customer.Address1 = "Fred";
ctx.UpdateObject(Customer);
if (!ctx.HasChanges()) return;
ctx.SaveChanges();

Хотя, как мы теперь знаем, это всегда будет сообщать HasChanges == true, так что вы можете также пропустить проверку.

Но не отчаивайтесь! Частичные классы, предоставляемые вашей справочной службой, могут быть расширены для выполнения именно того, что вы хотите. Это полностью стандартный код, так что вы можете написать.tt или какой-то другой коден. В любом случае, просто настройте это на свои сущности:

namespace ODataClient.ServiceReference1  // Match the namespace of the Reference.cs partial class
{
    public partial class Books  // Your entity
    {
        public bool HasChanges { get; set; } = false;  // Your new property!

        partial void OnIdChanging(int value)  // Boilerplate
        {
            if (Id.Equals(value)) return;
            HasChanges = true;
        }

        partial void OnBookNameChanging(string value)  // Boilerplate
        {
            if (BookName == null || BookName.Equals(value)) return;
            HasChanges = true;
        }
        // etc, ad nauseam
    }
    // etc, ad nauseam
}

Но теперь это прекрасно работает и так же выразительно для ОП:

var book = context.Books.Where(x => x.Id == 2).SingleOrDefault();
book.BookName = "W00t!";
Console.WriteLine(book.HasChanges);

НТН!

Возможно ли, что вы не добавляете / редактируете свои объекты должным образом? MSDN утверждает, что вы должны использовать AddObject, UpdateObject, или же DeleteObject чтобы включить отслеживание изменений на клиенте ( https://msdn.microsoft.com/en-us/library/gg602811(v=vs.110).aspx - см. Управление параллелизмом). В противном случае ваш метод расширения выглядит хорошо.

Я сомневаюсь, что datacontext в клиенте не то же самое. Так что изменения всегда false,

Вы должны быть уверены, что Datacontext один и тот же (экземпляр), для каждого изменения Datacontext, Тогда обнаруживать изменения имеет смысл.

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

КСТАТИ. Я использую код ctx.ChangeTracker.HasChanges() для обнаружения изменений DataContext.

    public bool IsContextDirty(DataServiceContext ctx)
    {
#if DEBUG
        var changed = ctx.ChangeTracker.Entries().Where(t => t.State != EntityState.Unchanged).ToList();
        changed.ForEach(
            (t) => Debug.WriteLine("entity Type:{0}", t.Entity.GetType()));
#endif
        return ctx != null && ctx.ChangeTracker.HasChanges();
    }

Чтобы это работало, автоматическое отслеживание изменений должно быть включено. Вы можете найти эту настройку в

ctx.Configuration.AutoDetectChangesEnabled

Все объекты сущности также должны отслеживаться контекстом ctx, Это означает, что они должны быть возвращены одним из ctxметоды или явно добавлены в контекст.

Это также означает, что они должны отслеживаться одним и тем же DataServiceContext, Вы как-то создаете более одного контекста?

Модель также должна быть настроена правильно. возможно Customer.Address1 не отображается в столбце базы данных. В этом случае EF не будет обнаруживать изменения в столбце.

Другие вопросы по тегам