Если в реализациях репозитория мы планируем использовать только O/RM, можем ли мы использовать подход TransactionScope вместо IUnitOfWork?

public interface IUnitOfWork
{        
    void Save();
}

Предполагая, что мы планируем когда-либо переключаться только между различными O/RM (доступ к этим O/RM инкапсулирован с помощью шаблона Repository), которые уже предоставляют свои собственные реализации шаблона Unit of Work, есть ли какие-либо причины, по которым мы не должны использовать TransactionScope подход вместо IUnitOfWork?

Спасибо


person user437291    schedule 07.01.2013    source источник


Ответы (2)


Реализация вашего IUnitOfWork может использовать любые технологические средства «транзакционализации» изменений между различными агрегатами за один раз, которые вы пожелаете, будь то TransactionScope, SqlTransaction или любое другое средство, которое вы выберете. Однако это не отменяет необходимости использования IUnitOfWork.

Репозитории предназначены для конкретного агрегата. Если вы одновременно сохраняете изменения только одного агрегатного типа, вы можете использовать только репозиторий. Но если вы одновременно сохраняете разные агрегаты и вам нужно, чтобы это постоянство было «транзакционизировано», чтобы изменения сохранялись все или ничего, то именно здесь в игру вступает шаблон «Единица работы».

В вашем примере мы скажем, что вы используете Linq в качестве своего OR/M, и вы сохраняете свои агрегаты в хранилище данных SQL, но хотите обернуть сохраняемость в TransactionScope. У вас может быть такое определение IUnitOfWork:

public interface IUnitOfWork 
{
  void MarkDirty(IAggregateRoot entity);
  void MarkNew(IAggregateRoot entity);
  void MarkDeleted(IAggregateRoot entity);
  void Commit();
  void Rollback();
}

Тогда ваша реализация может быть такой:

public LinqTransactionScopeUnitOfWork : IUnitOfWork
{
    public void Commit()
    {
        using (TransactionScope scope = new TransactionScope())
        {
            foreach (IAggregateRoot root in Changes)
            {
                //Save root based on how it was marked and what it's concrete type is using Linq.
            }

            scope.Complete();
        }
    }
}

Основная идея здесь заключается в том, что TransactionScope — это один из способов реализации IUnitOfWork, но наличие контракта Unit of Work позволяет вам предоставлять различные реализации в зависимости от вашей среды.

Надеюсь это поможет!

person Aaron Hawkins    schedule 07.01.2013
comment
Я предполагаю, что нередко используется несколько разных (то есть разных типов) реализаций IUnitOFWork одновременно, где каждая реализация используется в разных частях приложения? - person user437291; 08.01.2013
comment
У вас может быть или не быть несколько импл. в том же проекте, но что, если вы перенесете свой проект основного домена в другую среду, где TransactionScope недоступен, например, сделаете его клиентским приложением (где вы не можете рассчитывать на работу службы DTC) или перенесете его в мобильное приложение ? Наличие контракта позволяет вам предоставлять различные импл. на основе новой среды без необходимости переписывать основной проект домена, делая код домена более гибким и масштабируемым. - person Aaron Hawkins; 08.01.2013
comment
На ваш взгляд, должна ли реализация IUnitOfWork находиться на уровне BLL или DAL? - person user437291; 08.01.2013
comment
Зависит от архитектуры, но если вы используете архитектуру Onion или что-то подобное, где домен (BLL) не имеет зависимостей, контракт IUnitOfWork будет находиться в домене (BLL). Затем в проекте инфраструктуры приложения вы должны предоставить свою реализацию, соответствующую среде, для которой вы в настоящее время кодируете. Это справедливо и для репозиториев. - person Aaron Hawkins; 08.01.2013
comment
Отличный ответ. Спасибо вам обоим - person user437291; 09.01.2013

Есть одна техническая причина: у вас могут возникнуть трудности с использованием TransactionScope в вашей среде, вам нужно запустить службу и разблокировать порт брандмауэра. Если у вас нет этих проблем, я бы посоветовал вам использовать TransactionScope: это уже сделано/отлажено, почему вы думаете, что сделаете что-то лучше?

person remi bourgarel    schedule 08.01.2013
comment
Просто чтобы быть уверенным, вы предлагаете мне использовать TransactionScope напрямую (при условии, что у меня нет проблем с брандмауэром), без предварительного включения его в реализацию IUnitOfWork? - person user437291; 08.01.2013
comment
да, если вы делаете веб-приложение, вы можете создать его в своем Application_BeginRequest и зафиксировать его в конечном запросе. Зачем вам что-то обертывать, если вы ничего не добавляете? - person remi bourgarel; 08.01.2013
comment
Зачем вам обертывать что-то, если вы ничего не добавляете? Вы не согласны с тем, что аргументы, предоставленные Аароном Хокинсом, являются достаточно серьезным основанием для включения TransactionScope в IUnitOfWork? - person user437291; 08.01.2013
comment
Я не понимаю его точку зрения, как вы справляетесь с его решением, когда вам нужно вставить свои данные, а затем работать с ними? (например, вам нужен его идентификатор). вы не можете, потому что ваши объекты получат свой идентификатор только тогда, когда вы зафиксируете свою единицу работы. Это проблема чрезмерной инкапсуляции. - person remi bourgarel; 09.01.2013
comment
@remi bourgarel Если я правильно понимаю вашу ситуацию, реализация Unit of Work будет отвечать за сохранение AR, а также за получение и отслеживание идентификатора. Если у вас есть эта конкретная потребность, то реализация MarkDirty или MarkNew сделает ваше предварительное сохранение, получив ваш идентификатор и сохранив его в памяти, пока все не будет зафиксировано, как если бы вы использовали TransactionScope отдельно. - person Aaron Hawkins; 09.01.2013
comment
@ Аарон, да, вы можете создавать столько кода, сколько хотите, но, как я уже сказал, чем это может быть лучше, чем то, что уже сделано и отлажено, что вы можете использовать на уровне инфраструктуры (оставляя ваш бизнес-код чистым)? - person remi bourgarel; 09.01.2013
comment
Вопрос помечен как дизайн, ориентированный на предметную область, поэтому я должен предположить, что он ищет решение, которое соответствует преимуществам разработки решения, ориентированного на предметную область. Шаблон «Единица работы» предназначен для решения проблемы «Как транзакционировать изменения в нескольких сущностях одновременно?», с которой TransactionScope справляется сам по себе специфическим для инфраструктуры способом. Создание контракта позволяет решить проблему изоляции модели предметной области, что дает вам преимущества написания кода, ориентированного на предметную область. - person Aaron Hawkins; 09.01.2013
comment
если он действительно хочет, чтобы это было явно, то переносит это из инфраструктуры в бизнес-код. Но редко когда ваш клиент запрашивает транзакцию, он хочет, чтобы данные были чистыми, поэтому TransactionScope — это хороший способ сохранить ваши данные чистыми. - person remi bourgarel; 10.01.2013