How to create a LINQ to SQL Transaction?
I have a piece of code that involves multiple inserts but need to execute submitchanges method before I finish inserting in other tables so that I can aquire an Id. I have been searching through the internet and couldnt find how to create a transaction in linq to sql. I have put comments in the code where I want the transaction to take place.
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;
EDIT: Forgot to mention that ydc is my datacontext
Solution 1:
Wrap the whole thing in a TransactionScope
. Call transaction.Complete()
at the point where you want to commit. If the code exits the block without Complete()
being called, the transaction will be rolled back. However, after looking at @s_ruchit's answer and re-examining your code, you could probably rewrite this to not require a TransactionScope
. The first example uses the TransactionScope
with your code as is. The second example makes some minor changes, but accomplishes the same purpose.
A place where you would need to use the TransactionScope
is when you are reading a value from the database and using it to set a new value on an object being added. In this case the LINQ transaction won't cover the first read, just the later submit of the new value. Since you are using the value from the read to calculate a new value for the write, you need the read to be wrapped in the same transaction to ensure that another reader doesn't calculate the same value and obviate your change. In your case you are only doing writes so the standard LINQ transaction should work.
Example 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;
Example 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;
}
Solution 2:
You do not need to do explicit Transaction Implementation while using LINQ to SQL. All DB operations are wrapped in a transaction by default.
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();
All operations between db DataContext initialization and db.SubmitChanges() are wrapped around a Database Transaction by .Net ensuring your database to be in consistent and with property integrity maintained across tables.
Read an article By Scott Guthrie here :- http://weblogs.asp.net/scottgu/archive/2007/07/11/linq-to-sql-part-4-updating-our-database.aspx