Как создать транзакцию LINQ to SQL?

У меня есть фрагмент кода, который включает несколько вставок, но мне нужно выполнить метод submitchanges, прежде чем я завершу вставку в другие таблицы, чтобы я мог получить идентификатор. Я искал в Интернете и не мог найти, как создать транзакцию в linq to sql. Я добавил комментарии в код, в котором хочу, чтобы транзакция произошла.

    var created = false;
    try
    {
        var newCharacter = new Character();
        newCharacter.characterName = chracterName;
        newCharacter.characterLevel = 1;
        newCharacter.characterExperience = 0;
        newCharacter.userUsername = userUsername;
        newCharacter.characterClassID = ccslst[0].characterClassID;
        //Open transaction


            ydc.Characters.InsertOnSubmit(newCharacter);
            ydc.SubmitChanges();

            foreach (var ccs in ccslst)
            {
                var cs = new CharacterStat();
                cs.statId = ccs.statID;                        
                cs.statValue = ccs.statValue;
                cs.characterID = newCharacter.characterID;
                ydc.CharacterStats.InsertOnSubmit(cs);
            }                    


            var ccblst = ydc.ClassBodies.Where(cb => cb.characterClassID == newCharacter.characterClassID);
            foreach (var ccb in ccblst)
            {
                var charBody = new CharacterBody();
                charBody.bodyId = ccb.bodyId;
                charBody.bodyPartId = ccb.bodyPartId;
                charBody.characterID = newCharacter.characterID;
                ydc.CharacterBodies.InsertOnSubmit(charBody);
            }
            ydc.SubmitChanges();      
            created = true;
        //Commit transaction
        }
        catch (Exception ex)
        {
            created = false;
            //transaction Rollback;                    
        }
        return created;

РЕДАКТИРОВАТЬ: Забыл упомянуть, что ydc - мой текст данных


person Drahcir    schedule 16.04.2009    source источник


Ответы (2)


Оберните все это в TransactionScope. Вызовите transaction.Complete() в том месте, где вы хотите зафиксировать. Если код выходит из блока без вызова Complete(), транзакция будет отменена. Однако, посмотрев на ответ @ s_ruchit и повторно изучив свой код, вы, вероятно, могли бы переписать его, чтобы не требовать TransactionScope. В первом примере TransactionScope используется с вашим кодом как есть. Второй пример вносит некоторые незначительные изменения, но выполняет ту же цель.

Место, где вам может понадобиться использовать TransactionScope, - это когда вы читаете значение из базы данных и используете его для установки нового значения для добавляемого объекта. В этом случае транзакция LINQ не будет охватывать первое чтение, а только более позднюю отправку нового значения. Поскольку вы используете значение из чтения для вычисления нового значения для записи, вам нужно, чтобы чтение было заключено в ту же транзакцию, чтобы другой считыватель не вычислял то же значение и не допустил вашего изменения. В вашем случае вы делаете только записи, поэтому стандартная транзакция LINQ должна работать.

Пример 1:

var created = false;

using (var transaction = new TransactionScope())
{
    try
    {
        var newCharacter = new Character();
        newCharacter.characterName = chracterName;
        newCharacter.characterLevel = 1;
        newCharacter.characterExperience = 0;
        newCharacter.userUsername = userUsername;
        newCharacter.characterClassID = ccslst[0].characterClassID;

        ydc.Characters.InsertOnSubmit(newCharacter);
        ydc.SubmitChanges();

        foreach (var ccs in ccslst)
        {
            var cs = new CharacterStat();
            cs.statId = ccs.statID;                        
            cs.statValue = ccs.statValue;
            cs.characterID = newCharacter.characterID;
            ydc.CharacterStats.InsertOnSubmit(cs);
        }                    

        var ccblst = ydc.ClassBodies.Where(cb => cb.characterClassID == newCharacter.characterClassID);
        foreach (var ccb in ccblst)
        {
            var charBody = new CharacterBody();
            charBody.bodyId = ccb.bodyId;
            charBody.bodyPartId = ccb.bodyPartId;
            charBody.characterID = newCharacter.characterID;
            ydc.CharacterBodies.InsertOnSubmit(charBody);
        }
        ydc.SubmitChanges();      
        created = true;

        transaction.Complete();
    }
    catch (Exception ex)
    {
        created = false;
    }
}
return created;

Пример 2:

    try
    {
        var newCharacter = new Character();
        newCharacter.characterName = chracterName;
        newCharacter.characterLevel = 1;
        newCharacter.characterExperience = 0;
        newCharacter.userUsername = userUsername;
        newCharacter.characterClassID = ccslst[0].characterClassID;

        ydc.Characters.InsertOnSubmit(newCharacter);

        foreach (var ccs in ccslst)
        {
            var cs = new CharacterStat();
            cs.statId = ccs.statID;                        
            cs.statValue = ccs.statValue;
            newCharacter.CharacterStats.Add(cs);
        }                    

        var ccblst = ydc.ClassBodies.Where(cb => cb.characterClassID == newCharacter.characterClassID);
        foreach (var ccb in ccblst)
        {
            var charBody = new CharacterBody();
            charBody.bodyId = ccb.bodyId;
            charBody.bodyPartId = ccb.bodyPartId;
            newCharacter.CharacterBodies.Add(charBody);
        }
        ydc.SubmitChanges();      
        created = true;
    }
    catch (Exception ex)
    {
        created = false;
    }
person tvanfosson    schedule 16.04.2009
comment
Не уверен в синтаксисе, поместил ли я свой код в эту область: using (TransactionScope ts = new TransactionScope ()) {// мой код} - person Drahcir; 16.04.2009
comment
Вам потребуется запустить службу DTC на машине развертывания для выполнения транзакций с использованием TransactionScope. Держите это в уме. DTC: Служба координатора распределенных транзакций. - person this. __curious_geek; 16.04.2009
comment
@s_ruchit - на самом деле я не уверен, что с LINQ он перейдет в распределенную транзакцию. Поскольку контекст данных использует одно и то же соединение для всех отправлений, я думаю, что он останется локальным. Это отличается от использования адаптеров таблиц. - person tvanfosson; 16.04.2009
comment
@tvanfosson - Если вы используете TransactionScope, вам наверняка потребуется сервис DTC, но в случае LinqToSql он выполняет все операции с одним соединением, поэтому он не зависит от DTC. Я предпочитаю TransactionScope со вторым приоритетом, поскольку он зависит от DTC. - person this. __curious_geek; 16.04.2009
comment
@s_ruchit - Я почти уверен, что он не продвинется в DTC, если не задействовано более одного соединения. Я потратил много времени на отладку приложения TableAdapter, использующего TransactionScope, и отказывался от него только при определенных условиях. В конце концов мы определили, что это проблема с брандмауэром ... - person tvanfosson; 16.04.2009
comment
... и этот DTC нужно было открыть, когда задействовано более одного адаптера таблицы (внутри области была условная логика). Когда использовался только один настольный адаптер, он не продвигался, и мы не видели проблем. Хуже всего было то, что мы не узнали, пока не перешли в производство ... - person tvanfosson; 16.04.2009
comment
... потому что только рабочий сервер был защищен брандмауэром от моего веб-сервера. - person tvanfosson; 16.04.2009
comment
Да я согласен. dtc требуется только в том случае, если в транзакции участвует более одного соединения. если вы можете гарантировать, что другое соединение не будет создано внутри транзакции, тогда это будет работать нормально. Если вы используете другие компоненты внутри транзакции, убедитесь, что они не будут создавать никаких соединений, потому что это вызовет исключение, если DTC не включен. - person Mel; 31.01.2012
comment
Будет ли это работать, если я использую в своей транзакции разные объекты контекста данных? - person Evgeny Levin; 22.02.2012
comment
@EvgenyLevin использовать несколько контекстов для связи с одной и той же базой данных - плохая идея. У каждого контекста есть свои объекты, и я не думаю, что вы сможете заставить их хорошо играть вместе. Вы можете заставить их использовать соединение, но я думаю, у вас возникнут проблемы, если они попытаются работать с одними и теми же данными. - person tvanfosson; 22.02.2012
comment
TransactionScope из какого пространства имен: System.Activities.Statements или System.Transactions? Я ни разу раньше не пользовался. - person flipdoubt; 08.04.2013
comment
@flipdoubt, если вы используете Workflow Foundation, используйте System.Activities.Statements, в противном случае используйте System.Transactions. - person tvanfosson; 08.04.2013
comment
ПРЕДУПРЕЖДЕНИЕ! Значения по умолчанию для TransactionScope могут вызвать взаимоблокировки. Чтобы исправить это, используйте уровень изоляции ReadCommitted: blogs.msdn.com/b/dbrowne/archive/2010/05/21/ - person Thorgeir; 01.09.2014

Вам не нужно выполнять явную реализацию транзакции при использовании LINQ to SQL. По умолчанию все операции с БД заключаются в транзакцию.

Ex:

AppDataContext db = new AppDataContext();

<In memory operation 1 on db>
<In memory operation 2 on db>
<In memory operation 3 on db>
<In memory operation 4 on db>

db.SubmitChanges();

Все операции между инициализацией db DataContext и db.SubmitChanges () обертываются вокруг транзакции базы данных с помощью .Net, обеспечивая согласованность вашей базы данных и поддержание целостности свойств в таблицах.

Прочтите статью Скотта Гатри здесь: - http://блоги.asp.net/scottgu/archive/2007/07/11/linq-to-sql-part-4-updating-our-database.aspx

person this. __curious_geek    schedule 16.04.2009
comment
Но нужен был идентификатор, который был сгенерирован при первой отправке изменений, иначе не знал, как получить идентификатор. - person Drahcir; 16.04.2009
comment
Посетите статью, написанную ScottGu. Вы можете подключиться без идентификатора. В этом прелесть LINQ-to-SQL. Если вы так не поступаете, значит, вы не используете Linq-To-Sql. Рекомендую прочитать статью ScottGu. Вы можете найти ссылку в моем ответе. - person this. __curious_geek; 16.04.2009
comment
да. Похоже, вы могли бы переписать это, чтобы не требовать внешней транзакции. Я обновлю свой ответ. - person tvanfosson; 16.04.2009
comment
+1 похоже, что LINQ to SQL решает все зависимости за вас при фиксации. Хороший :) - person satnhak; 06.11.2011
comment
Значит, если какие-то изменения приведут к сбою, все предыдущие изменения будут отменены? - person VSB; 10.02.2018