Использование TransactionScope на уровне обслуживания для операций UnitOfWork

Правильно ли мой подход объединить все 3 метода dataprovider.GetXXX в TransactionScope на уровне сервиса как UnitOfWork?

Вы бы сделали что-нибудь другое?

Откуда TransactionScpe знает конкретную ConnectionString?

Должен ли я получить объект Transaction из моего соединения и передать этот объект Transaction конструктору TransactionScope?

Уровень обслуживания, такой как AdministrationService.cs

private List<Schoolclass> GetAdministrationData()
{

   List<Schoolclass> schoolclasses = null
   using (TransactionScope ts = new TransactionScope())
                    {
                        schoolclasses = _adminDataProvider.GetSchoolclasses();
                        foreach (var s in schoolclasses)
                        {
                           List<Pupil> pupils = _adminDataProvider.GetPupils(s.Id);
                           s.Pupils = pupils;

                           foreach (var p in pupils)
                           {
                               List<Document> documents = _documentDataProvider.GetDocuments(p.Id);
                               p.Documents = documents;
                           }
                        } 

                        ts.Complete();
                    }


   return schoolclasses;
}

Пример того, как может выглядеть любой из этих трех методов в DataProvider:

public List<Schoolclass> GetSchoolclassList()
        {
            // used that formerly without TransactionSCOPE => using (var trans = DataAccess.ConnectionManager.BeginTransaction())
            using (var com = new SQLiteCommand(DataAccess.ConnectionManager))
            {
                com.CommandText = "SELECT * FROM SCHOOLCLASS";

                var schoolclasses = new List<Schoolclass>();

                using (var reader = com.ExecuteReader())
                {
                    Schoolclass schoolclass = null;
                    while (reader.Read())
                    {
                        schoolclass = new Schoolclass();
                        schoolclass.SchoolclassId = Convert.ToInt32(reader["schoolclassId"]);
                        schoolclass.SchoolclassCode = reader["schoolclasscode"].ToString();
                        schoolclasses.Add(schoolclass);
                    }
                }
                // Used that formerly without TransactionSCOPE => trans.Commit();
                return schoolclasses;
            }
        }

person msfanboy    schedule 09.02.2011    source источник
comment
TransactionScope обычно следует использовать при изменении данных или другой операции (не связанной с базой данных), которая должна быть операцией типа «все или ничего». Выбор данных из базы данных на самом деле не является транзакцией. Кроме того, если вам нужны все эти результаты, вы должны получать несколько наборов результатов в одном запросе к базе данных. В вашем случае, поскольку вы не используете хранимые процедуры, у вас должно быть несколько операторов выбора в одном запросе (CommandText). После этого у DataReader будет несколько наборов результатов, которые можно будет перебирать с помощью метода NextResult () DbDataReader.   -  person Shiv Kumar    schedule 10.02.2011
comment
TransactionScope делает себя известным (или наоборот) базовому поставщику ADO.NET, так что все, что вы выполняете в области действия / времени существования TransactionScope, автоматически участвует в транзакции. Таким образом, его намного проще использовать, чем классический API-интерфейс connection.BeginTransaction ().   -  person Morten Mertner    schedule 10.02.2011
comment
@Shiv ok, мой образец был немного тупым с выбором ... для обновления, вставки, удаления его действительного. Да, я знаю о методе .NextResult () загрузчика данных, но есть одна причина, по которой я не могу его использовать. Предположим, я получил все классы Schoolclasses в первом операторе выбора. как передать EACH schoolclassId набора результатов второму оператору select, чтобы получить учеников для EACH SchoolclassId? Не использую магазинные процедуры. Я использую простой комментарий. Текст = Выбрать .... поля из таблицы   -  person msfanboy    schedule 10.02.2011
comment
пример: com.CommandText = Выбрать * ИЗ Школьного класса; Выберите * от ученика; Выберите * из документов; Это очень простой образец на самом деле. У меня есть отношение N: M от учеников к документам ... @Shiv позволяет предположить, что у меня есть эти 3 ResultSets в считывателе и я выполняю 2 раза reader.NextResult () я прав, что у меня все еще есть сравнить schoolclassId со значением pupil.schoolclassId, чтобы узнать, какой ученик принадлежит к какому набору schoolclass.Pupils?   -  person msfanboy    schedule 10.02.2011
comment
@Shiv плохая вещь с набором результатов была бы в том, что мне пришлось бы получить ВСЕ документы, потому что в тот момент, когда я строю запрос с 3 операторами Select, я не знаю каждого отдельного идентификатора ученика, связанного с таблицей PUPIL_DOCUMENT, которая снова относится к таблице DOCUMENT . Итак, насколько я понимаю, для меня нет ResultSet.   -  person msfanboy    schedule 10.02.2011
comment
@msfanboy, не пытаясь вдаваться в подробности, которые вы предоставили ... используйте оператор соединения, и вам, вероятно, понадобится только один набор результатов. Если вы все время полагаетесь на выборку из одной таблицы, значит, вы не используете базу данных для того, что она действительно хорошо умеет делать.   -  person Shiv Kumar    schedule 10.02.2011
comment
@Shiv Ты понял! На самом деле это вы предложили сделать 3 выбора в одном запросе ;-) Я не занимаюсь такими вещами. Я делаю 3 левых соединения на своих трех таблицах и вижу, что все школьные классы, ученики, документы видят это:   -  person msfanboy    schedule 10.02.2011
comment
com.CommandText = SELECT schoolclassId, schoolclasscode, pupilId, имя, фамилия, улица, почта, город, телефон, электронная почта, дополнительная информация, пол, schoolclassId_FK, documentId, имя документа, documentId_FK ИЗ ШКОЛЬНОГО КЛАССА LEFT OUTER JOIN Passschool.schoolclass.schoolclass LEFT OUTER JOIN PUPIL_DOCUMENT ON PUPIL.pupilId = PUPIL_DOCUMENT.pupilId_FK LEFT OUTER JOIN DOCUMENT ON PUPIL_DOCUMENT.documentId_FK = DOCUMENT.documentId   -  person msfanboy    schedule 10.02.2011
comment
@msfanboy, ваш пример кода показывает несколько вызовов базы данных: GetSchoolClasses (), затем набор GetPupils (), затем набор GetDocuments (), а затем вы показываете образец того, как выглядят вызовы базы данных. Так чего вы ожидаете? ;)   -  person Shiv Kumar    schedule 10.02.2011
comment
@Shiv Я ожидал, что ты умнее меня ;-)   -  person msfanboy    schedule 10.02.2011


Ответы (1)


Это выглядит нормально - это то, для чего TransactionScope существует, чтобы обеспечить контроль транзакций в вашем коде (и это общий шаблон для UoW).

Откуда TransactionScpe знает конкретную ConnectionString?

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

Должен ли я получить объект Transaction из моего соединения и передать этот объект Transaction конструктору TransactionScope?

Нет нет нет. См. Выше. Просто делай то, что делаешь сейчас. Нет вреда в вложении TransactionScopes.

person Oded    schedule 09.02.2011
comment
хорошо Одед, но у меня нет доступа к нескольким базам данных, просто обновляю / удаляю / вставляю в несколько таблиц - хорошо, я знаю, что выбираю здесь ... но просто забудь об этом сейчас ;-) - - person msfanboy; 10.02.2011
comment
@msfanboy - Не в этом дело. Вы используете его точно так же с единственной базой данных. Мой ответ стоит ... - person Oded; 10.02.2011
comment
@Oded Итак, класс TransactionScope также избавляет меня от использования методов сохранения / сохранения бизнес-уровня, где у меня всегда есть параметр транзакции, верно? На самом деле да, так что этот класс очень хороший, тогда хе-хе. - person msfanboy; 10.02.2011