5

Will the below code rollback the changes if there are any exception while saving?

using (SampleEntities context = new SampleEntities())
{
    //Code Omitted

    context.EmpPercAdjustments.AddRange(pp);
    context.SampleJobs.AddRange(sampleJobs);

    context.SaveChanges();
}

Or

Do I need to use transaction?

using (SampleEntities context = new SampleEntities())
{
    //Code Omitted

    using (System.Data.Entity.DbContextTransaction tranc = context.Database.BeginTransaction( ))
    {
        try
        {
            context.EmpPercAdjustments.AddRange(pp);
            context.SampleJobs.AddRange(sampleJobs);
            context.SaveChanges();
            tranc.Commit();
        }
        catch (Exception ee)
        {
            tranc.Rollback();
        }
    }
}

Are there any advantages of using one over the others?

6
  • Have you tried it? Commented Feb 20, 2017 at 16:01
  • @CallumLinington Yes I tried it. Looks like both code rollsback if there are any exception. Commented Feb 20, 2017 at 16:03
  • 1
    From my understanding, which could be wrong, you are in essence not changing HOW the data saves when it is successful, just how it is represented and saved when it fails. When you scope a transaction you are setting an operation on the database(or other data source) to say: "You two things are now one thing". This is commonly done when you must save two things of data that are interdependent on each other. Like say you save a transaction record that also has a dependent audit record you save both. If one fails, they both do, so you scope both in a single transaction.
    – djangojazz
    Commented Feb 20, 2017 at 16:30
  • 1
    I should say you are having the potential to handle what happens when it fails. By default .NET does it's own setting and you have already put in a try catch in your example for a rollback. If you were missing that, I don't know what the default behavior would do, it may rollback or it may just have a non committed transaction.
    – djangojazz
    Commented Feb 20, 2017 at 16:34
  • 1
    When you have a chain of Save's to the database, you have a choice on how you handle exceptions. I generally take the root of either ignore (which can cause issues) or compensation (which means compensatory actions based on what has been done). In order to apply compensation, you need to keep track on what was successful and unsuccessful. This isn't hard in this scenario because the object that you save to the database will contain (after the save) the id it was given. Commented Feb 20, 2017 at 16:38

1 Answer 1

9

Yes it will rollback correctly.

In this case, you do not need to run an explicit transaction, because there is one already created by Entity Framework.

Creating a transaction by calling context.Database.BeginTransaction() is good, if you want f.e. to get the Id of just inserted record, something like this:

using (SampleEntities context = new SampleEntities())
{
    using (System.Data.Entity.DbContextTransaction trans = context.Database.BeginTransaction( ))
    {
        context.SampleJobs.Add(newJob);
        context.SaveChanges();
        var jobId = newJob.Id;
        //do other things, then commit or rollback
        trans.Commit();
    }
}

In this case, after calling SaveChanges(), the changes made on context objects are applied (so you can read database generated Id of added object in your scope), but they still have to be commited or rolled back, because changes are only dirty written.

Defining an explicit transaction can also be useful, if you have multiple methods that can modify context objects, but you want to have a final say, if changes they made will be all commited or not.

3
  • I would be very clear though with your comment '//do other things...' that the user MUST be committing or rolling back. Uncommitted transactions that stick around are dangerous as they can bring in unintended consequences later. Say you have a dirty save and then someone else a few seconds later does a rollback, you the potential to rollback your values. Or you could wrap this in another commit which would probably not affect anything negatively but had the potential for weird things on restores to happen.
    – djangojazz
    Commented Feb 20, 2017 at 16:49
  • 1
    Yes, you should always commit or rollback such a transaction. But if you will not, the rollback will be called when destroying trans object that has not been committed (this will be performed after leaving the using block) Commented Feb 20, 2017 at 17:05
  • Interesting I never head that before, I will have to check that later with some test code and confirm that.
    – djangojazz
    Commented Feb 20, 2017 at 17:20

Not the answer you're looking for? Browse other questions tagged or ask your own question.