Простые транзакции в EF4.0 POCO с шаблоном UnitOfWork и ApplicationBus - PullRequest
0 голосов
/ 11 октября 2011

Я недавно столкнулся с удивительным поведением EF4, когда после добавления сущности в контекст она недоступна для запросов (ну, вам нужно сделать так, чтобы ваши запросы знали, что вы можете искать в памяти), кроме SaveChanges ().

Позвольте мне немного объяснить наш сценарий: мы используем шаблон UnitOfWork с объектами EF 4.0 и POCO.Недавно мы решили внедрить Message Bus , где мы будем реализовывать в обработчиках сообщений большую часть логики приложения.

Проблема, с которой я столкнулся, заключалась в том, что я передавал свой UnitOfWork (контекстная обертка в нашем случае) в сообщениях.Например, у меня есть логика печати штрих-кода, когда я делаю это, он должен изменить счетчик печати в БД.Печать может выполняться в произвольном порядке для существующего пакета или может выполняться автоматически при создании пакета особого типа.Я прохожу через UnitOfWork, а затем ищу штрих-код с:

public void Handle(IBarCodePrintMessage message)
{
    if (message.UnitOfWork == null)
        using (var uow = factory.Create<IUnitOfWork>)
        {
             Handle(message, uow);
             uow.Commit();
        }
    else
        Handle(message, message.UnitOfWork);
}

void Handle(IBarCodePrintMessage message, IUnitOfWork uow)
{
    // the barcode with the PackageID is in the context or in the db
    var barCode = uow.BarCodes.Where(b => b.PackageID == message.PackageID).SingleOrDefault();

    barCode.IncreasePrintCount(); // this might be actually quite complex, and sometimes fail
    printingServices.PrintBarCode(barCode);
}

Моя проблема в том, что если штрих-код был добавлен в это же самое время, а фиксации еще не было, то это не такнайдено.

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

У меня есть пара вопросов на самом деле:

1) Я думал о том, чтобы каким-то образом взломать мой IUnitOfWork, чтобы вернуть набор объектов, которые могут находиться либо в памяти (не зафиксированные изменения), либо в БД (если еще не получены).На самом деле это поведение, которое я ожидаю от моего UnitOfWork, скажите мне, в каком состоянии я нахожусь в последний раз, даже если я еще не зафиксировал, и не предоставляю мне состояние db.Для состояния БД я могу создать другой контекст.

В любом случае это кажется довольно сложным, потому что мне нужно будет реализовать свой собственный тип коллекции сущностей, все методы расширения для него (где сначала выберите select,groupby и т. д.), а затем заставить его работать способом IQueryable (то есть сразу не перечислять таблицы), а затем найти способ сопоставления локально кэшированных и извлеченных сущностей.Мне кажется, это общая проблема, и я думаю, что уже есть реализация, но я не уверен, где.

2) другой вариант - вводить транзакции вручную.Я попробовал его в отношении SQLCE4.0, и это могло бы решить проблему (очень часто вызывать savecontext, чтобы объекты можно было запрашивать из БД, а затем, если возникла какая-либо ошибка, откатить транзакцию), но у меня большие сомнения.Могут быть разные потоки, выполняющие разные транзакции одновременно, не уверен, как они будут взаимодействовать, если откат выполняется.

Также мы используем SQL CE 4.0 и SQL Express 2008 (и то, и другое можно динамически переключать).Начало обработки транзакций таким образом, кажется, приводит к появлению кода DTC, который - я читаю везде - довольно тяжелая вещь, и я бы предпочел избегать.Есть ли способ использовать транзакции простым способом без риска использовать их в DTC?

3) У кого-нибудь есть какие-либо другие варианты или идеи о том, как решить эту проблему?

1 Ответ

0 голосов
/ 12 октября 2011

Вы можете запросить контекст для непостоянных сущностей.

var entity = context.ObjectStateManager
                    .GetObjectStateEntries(EntityState.Added)
                    .Where(e => !e.IsRelationship)
                    .Select(e => e.Entity)
                    .OfType<BarCode>()
                    .FirstOrDefualt(b => b.PackageId == message.PackageId);

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

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

...