#c# #transactions #business-logic #transactionscope
#c# #транзакции #бизнес-логика #transactionscope
Вопрос:
Я собираюсь создать сервис, используя 3-уровневую архитектуру, и я действительно беспокоюсь о том, как обрабатывать операции транзакционным способом.
Я знаю, что у меня есть 2 варианта: IDbTransaction
и TransactionScope
… но я на самом деле не решил, какой из них выбрать, хотя я провел много исследований.
Я бы выбрал TransactionScope, но я не хочу привлекать DTC … плюс мне нужна поддержка SQLServer2005 и Oracle. (Я знаю, что мне нужно открывать только одно соединение одновременно)
Я хотел бы увидеть хорошие примеры / шаблоны их использования для обоих случаев… Хорошие ссылки были бы просто великолепны.
Примерно так могли бы выглядеть классы BL и DAL… но также и то, как создаются транзакции / соединения и передаются между ними.
Правка 1: Я ищу какую-то реализацию этого (но для обоих вариантов):
using(var scope = new TransactionScope())
{
// transactional methods
datalayer.InsertFoo();
datalayer.InsertBar();
scope.Complete();
}
Правка2:
Поскольку Денис предложил мне очень хорошую альтернативу… Я все еще жду, когда кто-нибудь покажет мне хороший пример модели с взаимодействием между бизнес-уровнем и уровнем данных с использованием ‘ TransactionScope
‘
Спасибо.
Комментарии:
1. Я вижу, что у вас также возникают трудности с тем, как структурировать ваш уровень данных в первую очередь, и лучшим ресурсом для этого действительно является книга Фаулера «Шаблоны архитектуры корпоративных приложений». Ознакомьтесь с этим, прочтите от корки до корки
2.используете ADO.NET ?
3. на момент написания статьи, да, я использовал ADO.NET ; теперь у нас также есть EF6
Ответ №1:
В настоящее время я использую в этом случае несколько репозиториев и множественный подход UnitOfWork. Допустим, у вас есть CustomerRepository и InvoiceRepository. Если вам нужно сделать это:
customerRepository.Add(customer);
invoiceRepository.Add(bill);
и если эти две транзакции являются транзакцией, то при создании репозитория я предоставляю им ту же единицу работы, что и:
IUnitOfWork uow = UnitOfWork.Start();
ICustomerRepository customerRepository = new CustomerRepository(uow);
IInvoiceRepository invoiceRepository = new InvoiceRepository(uow);
таким образом, приведенные выше утверждения теперь:
customerRepository.Add(customer);
invoiceRepository.Add(bill);
uow.Commit();
Вся магия находится внизу, зависит от того, что вы используете в качестве технологии обработки данных (либо ORM, такой как NHibernate, либо, возможно, raw ADO.NET — и в большинстве случаев это не рекомендуется).
Для получения хорошего примера по шаблону репозитория и UnitOfWorks ознакомьтесь с этим руководством, но обратите внимание, что в нем вы не можете иметь несколько активных UnitOfWorks (и на самом деле это нужно немногим приложениям, поэтому здесь нет реальной проблемы). Кроме того, в руководстве используется NHibernate, поэтому, если вы не знакомы с концепцией ORM, я предлагаю вам ознакомиться с ней (если позволяет ваше расписание).
еще одна вещь: здесь также есть более продвинутые шаблоны, такие как сеанс на разговор и тому подобное, но это продвинутый материал, над которым я сейчас работаю, если вы хотите взглянуть на проекты uNhAddIns (также для NHibernate)
Комментарии:
1. Большое спасибо за вашу рекомендацию. Это действительно хороший материал. На самом деле, я бы сказал, это идеально. К сожалению, я не думаю, что смогу перейти на ORM прямо сейчас, но, возможно, «Entity Framework» позже. На данный момент я буду придерживаться ADO.NET и, возможно, я попытаюсь перенять некоторые материалы, описанные в отправленной вами ссылке (если смогу найти способ).
2. Ну, на самом деле это не сложно, и я вижу, что вы думаете в правильном направлении. UnitOfWork / репозитории, все это выполнимо с ADO.NET кроме того, дело в том, что вы потратите много времени на создание действительно структурированного уровня данных, подобного этому, потому что вы не захотите использовать простые повторяющиеся методы CRUD. Если вы собираетесь использовать «более простой» способ raw ADO.NET , чем пытаться использовать шаблоны шлюза табличных данных / табличных модулей с привязкой данных к таблицам данных, которые . Мощная поддержка NET (не используйте привязку VS designer, используйте привязку вручную). Оба шаблона описаны в книге PoEAA Фаулера.
3. Попробуйте абстрагироваться от ADO.NET слой также, имея что-то вроде: SqlExecutor с: IsolationLevel IsolationLevel { get; set; } void StartTransaction(); void Commit(); void Rollback(); void ExecuteNonQuery(строка sql); object ExecuteScalar(строка sql); и т.д…
4. «»Попробуйте абстрагироваться от ADO.NET уровень также «» — это именно то, что у меня частично уже есть … и дает мне хорошую отправную точку для разных серверов БД. Однако у меня недостаточно гибкий механизм обработки транзакций за пределами уровня данных.
5. Проблема с этим подходом заключается в том, что класс service, вызывающий репозитории, скрывает свои зависимости, поскольку ему необходимо создать экземпляры всех из них (репозиториев) на основе активного экземпляра единицы работы.
Ответ №2:
Я бы выбрал TransactionScope, потому что он намного проще в использовании, поскольку вам не нужно переносить объект транзакции или передавать его каждому методу. Это окружающая среда. Это означает, что в большинстве случаев разработчики могут почти забыть о транзакциях, написать классные бизнес-ориентированные методы, а затем, позже, добавлять оболочки транзакций (используя ‘using’) там, где это действительно необходимо, впоследствии. (звучит идиллически, я знаю, но это почти так).
Вопреки распространенному мнению, использование TransactionScope не означает, что MSDTC будет задействован, смотрите здесь краткое описание этого:
Избегайте нежелательной эскалации до распределенных транзакций
И, если вам действительно нужна распределенная транзакция, как вы планируете это сделать без MSDTC в любом случае? Что интересно в TransactionScope снова, так это то, что при необходимости он будет расширен до MSDTC, ничего не меняя в вашем коде.
Комментарии:
1. Спасибо Саймону. Я действительно ценю… но мой вопрос требует также примера того, как именно мне нужно было бы использовать transactionscope внутри бизнес-уровня. «звучит идиллически, я знаю»… для меня это именно так, потому что я действительно не понимаю, как это должно быть сделано. Знаете ли вы, по крайней мере, хорошие ссылки на то, как это использовать (бизнес-уровень, уровень данных, TransactionScope — в целом)
2. @Cristi — На самом деле, он довольно прост в использовании, как и приведенный вами пример кода, вы оборачиваете любой код (business или что-либо еще) директивой using. В этом вся цель -:) Что вы ищете помимо стандартных результатов Google?
3. Я знаю… Я чувствую себя довольно глупо… Но я хотел бы знать больше, например: как и где создать SqlConnection (чтобы быть уверенным, это только один для SqlServer2005, чтобы не наращивать). Я ищу ссылку или хороший пример того, как структурировать и реализовать уровень данных (и их сущности) и операции бизнес-уровня (используя область транзакции). Вы знаете хорошую ссылку или что-то в этомроде?
4. Как вы думаете, мне следует отредактировать свой вопрос, есть ли что-то неясное из того, чего я хочу? … Странно, что я не получаю никакого ответа 🙂
Ответ №3:
Вы можете абстрагировать эти две технологии, внедрив тонкую оболочку, которую вы используете в своем бизнес-слое. Давайте скажем что-то вроде:
using (IUnitOfWork unitOfWork = UnitOfWork.Start())
{
// do something
unitOfWork.Commit();
}
Затем вы можете использовать реализацию, которая просто открывает транзакцию базы данных. Если вам нужно задействовать другую транзакционную систему, вы просто переключаете реализацию unit of work на ту, которая открывает TransactionScope, а не простую транзакцию базы данных.
Ответ №4:
Шаблон UnitOfWork идеально подходит для четкой обработки транзакций (среди прочего). Вот хорошая реализация шаблона, используемого в некоторых высокопроизводительных приложениях:
https://github.com/NikGovorov/Taijutsu/tree/master/Sources/Sources/Taijutsu-Infrastructure
Хотя вам пришлось бы реализовать dataProvider для ORM по вашему выбору (NH, EF, …), но это довольно тривиально.
Комментарии:
1. да, это случается. вот еще один: github.com/Kostassoid/Anodyne/tree/master/src/main /…