1

I'm using an Entity Framework context, and sometimes would like to create a transaction so that the client can do multiple changes and then commit/rollback them all together. However, if many changes were made, and SaveChanges() was called multiple times, after which a rollback is called, entities that were commited already, will remain the way they were commited, and would not roll back.

Here's how my transaction-relative functions look like:

Public Sub BeginTransaction()
    Dim context = HttpContext.Current.GetObjectContext()
    If Not context.Connection.State = ConnectionState.Open Then
        context.Connection.Open()
    End If
    Dim trasnaction = context.Connection.BeginTransaction()
    HttpContext.Current.Items("transaction") = trasnaction
End Sub

Public Sub CommitTransaction()
    Dim transaction = CType(HttpContext.Current.Items("transaction"), DbTransaction)
    transaction.Commit()
    HttpContext.Current.Items("transaction") = Nothing
End Sub


Public Sub RollbackTransaction()
    Dim transaction = CType(HttpContext.Current.Items("transaction"), DbTransaction)
    transaction.Rollback()
    HttpContext.Current.Items("transaction") = Nothing
End Sub

And then I run next test:

Public Sub TransactionsTest()
    Dim context = GetObjectContext()
    Dim firstString = context.LocalizedStrings.Where(Function(ls) ls.ID = 4).First()
    Dim secondString = context.LocalizedStrings.Where(Function(ls) ls.ID = 5).First()

    Dim firstStringOriginal = firstString.Text
    Try
        DatabaseProvider.BeginTransaction()
        firstString.Text = "blabla"
        context.SaveChanges() //Saves OK
        Dim caughExeption = False
        Try
            secondString.LanguageID = "suka"
            context.SaveChanges() //Doesn't save, because change is invalid
            DatabaseProvider.CommitTransaction()
        Catch ex As Exception
            DatabaseProvider.RollbackTransaction()
            caughExeption = True
        End Try
        Assert.IsTrue(caughExeption)
        // Try to retrieve the first string again
        firstString = context.LocalizedStrings.Where(Function(ls) ls.ID = 4).First()
        Assert.AreEqual(firstStringOriginal, firstString.Text) // FAILS :(
    Finally
        firstString.Text = firstStringOriginal
        context.SaveChanges()
    End Try
End Sub

Any help will be much appreciated, thanks.

Not only does the test fail, but it also leave the ObjectContext in an unstable mode, because it still has invalid changes in it.

Michael Maddox
  • 11,633
  • 5
  • 35
  • 39
Svarog
  • 2,128
  • 13
  • 19
  • I think you need to reset the entity state manually in addition to rolling back any transactions. – Xhalent Aug 23 '11 at 09:47
  • 1
    why C# tag when the code is in VB? Its giving me a headache trying to read it. ;) – lahsrah Aug 23 '11 at 09:59
  • C# tag because C# and VB are the same in this regard. I too am a C# person, but am forced to write this project in VB :( – Svarog Aug 23 '11 at 11:50

1 Answers1

4

It has nothing to do with transactions. It is how EF behaves. You are using the same context instance for loading the entity => you will get your first (modified but not persisted) instance of the entity despite the data in the database. That happens because of core ORM concept called identity map. If you want to get real data from database you must instruct EF to overwrite your changes:

context.LocalizedStrings.MergeOption = MergeOption.OverwriteChanges
firstString = context.LocalizedStrings.Where(Function(ls) ls.ID = 4).First()
Community
  • 1
  • 1
Ladislav Mrnka
  • 349,807
  • 56
  • 643
  • 654
  • I'm afraid this is not sufficient. The context is still in a broken state, as invalid changes are still in action. What I need is an option to rollback the stored Entities when rolling back a transaction. – Svarog Aug 23 '11 at 11:47
  • Context will not rollback. You must manually revert any changes you did. The easiest way is disposing your context and loading your entities again in the new context instance. – Ladislav Mrnka Aug 23 '11 at 12:10