Долгоживущий контекст Entity Framework - наилучшая практика, позволяющая избежать проблем с целостностью данных - PullRequest
5 голосов
/ 28 декабря 2011

В качестве очень элементарного сценария, давайте возьмем эти две операции:

UserManager.UpdateFirstName(int userId, string firstName)
{
    User user = userRepository.GetById(userId);
    user.FirstName = firstName;
    userRepository.SaveChanges();
}

InventoryManager.InsertOrder(Order newOrder)
{
    orderRepository.Add(newOrder);
    orderRepository.SaveChanges();
}

Я использовал EF только в веб-проектах и ​​в значительной степени полагался на природу Интернета без состояния. При каждом запросе я получал бы свежую копию контекста, внедренную в мои фасадные объекты бизнес-уровня (сервисы, менеджеры, как бы вы их ни называли), причем все бизнес-менеджеры делят один и тот же экземпляр контекста EF. В настоящее время я работаю над проектом WPF и внедряю бизнес-менеджеров, а затем и репозитории, которые они используют, непосредственно в модель представления.

Давайте предположим, что пользователь находится на сложном экране, и его первое нажатие кнопки вызывает метод UpdateFirstName (). Давайте предположим, что SaveChanges () не работает по любой причине. Их второе нажатие кнопки вызовет метод InsertOrder ().

В Интернете это не проблема, поскольку контекст для операции # 2 не связан (новый http-запрос) с контекстом, используемым операцией # 1. На рабочем столе, однако, это тот же контекст между обоими действиями. Проблема возникает в том факте, что имя пользователя было изменено и как таковое отслеживается контекстом. Несмотря на то, что оригинальный SaveChanges () не выполнял (скажем, база данных была недоступна в то время), вторая операция, вызывающая SaveChanges (), не только вставит новый порядок, но и обновит имя пользователя. Почти во всех случаях это нежелательно, так как пользователь давно забыл о своем первом действии, которое все равно не удалось.

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

Как вы, ребята, справляетесь с такими ситуациями?

Ответы [ 3 ]

3 голосов
/ 28 декабря 2011

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

Ответ, который, кажется, соответствует философии ORM, заключается в том, чтобы иметь контекст, который имеет тот же срок службы, что и экран.,В сущности, контекст данных является реализацией Unit Of Work.Для веб-приложения это, очевидно, запрос, и он хорошо работает.

В настольном приложении вы можете привязать контекст к экрану или, возможно, привязать к операции редактирования (между загрузкой и нажатием «сохранить»).«).Операции только для чтения могут иметь одноразовый контекст (использование идентификаторов для перезагрузки объекта при необходимости, например, при нажатии «кнопки удаления» в сетке).Забудьте о контексте, который охватывает всю жизнь приложения, если вы хотите быть в курсе изменений от других пользователей (коллекции отношений кэшируются при первой загрузке).Также забудьте о прямом совместном использовании сущностей EF между различными окнами, потому что это эффективно делает его общедоступным и, следовательно, долгоживущим контекстом.

Мне кажется, что ORM стремятся реализовать веб-дизайн в настольных приложениях из-заэтот.Боюсь, нет никакого способа обойти это.Не то, чтобы это было плохо само по себе.Обычно вы не должны атаковать базу данных на уровне рабочего стола, если она должна использоваться несколькими экземплярами.В этом случае вы будете использовать сервисный уровень, EF будет в его элементе, и ваша проблема будет перенесена в «сервисный контекст» ...

2 голосов
/ 28 декабря 2011

У нас есть большое приложение WPF, которое вызывает службы WCF для выполнения операций CRUD, таких как UpdateFirstName () или InsertOrder ().Каждый вызов в сервисе создает новый ObjectContext, поэтому нам не нужно беспокоиться о несогласованности ObjectContext, висящей вокруг.

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

Предположим, что вы создаете новый ObjectContext каждый раз, когда метод вызывается в вашем хранилище, приведенный ниже фрагмент все равно предоставит вам поддержку транзакций с использованием класса TransactionScope.Например,

        using (TransactionScope scope = new TransactionScope())
        {
            UserManager.UpdateFirstName(userid,firstName);
            InventoryManager.InsertOrder(newOrder);

            scope.Complete();
        }
0 голосов
/ 03 июля 2015

Одним совершенно другим способом является запись всех изменений в базу данных напрямую (с кратковременным контекстом базы данных). А затем добавьте какой-нибудь черновик / версионирование поверх этого. Таким образом, вы все равно можете предложить функциональность ok / отменить / отменить.

...