Jim Rogers

Lives in Baton Rouge, LA, with two dogs, one cat, and one lovely wife. I'm a lead developer for GCR Incorporated.

Katrin and Jim

Month List

DbContext with linked servers

by Jim Aug 28, 2012 7:00 PM

Weird things happen when you try to use DbContext to update rows on a synonym which points to a linked server.

System.Data.Entity.Infrastructure.DbUpdateException: An error occurred while updating the entries. See the inner exception for details. ---> System.Data.UpdateException: An error occurred while updating the entries. See the inner exception for details. ---> System.Data.SqlClient.SqlException: Unable to start a nested transaction for OLE DB provider "SQLNCLI10" for linked server "WebSQL". A nested transaction was required because the XACT_ABORT option was set to OFF.

If the changes can be rolled into a stored procedure, this is one solution; SET XACT_ABORT ON can be called at the beginning of the procedure. But this may not be simple, and kind of defeats the purpose of using Entity Framework.

Another solution is to set the XACT_ABORT option globally, for the server instance. This is a bit extreme. I actually found this little error when we turned that option OFF (the default) – it had been on, but that was breaking some Microsoft accounting software that lived on the server in question. So change this at the server level at your own risk…

I wrote a little class to set XACT_ABORT ON for my connection. It’s used like this:

Using New TransactAbortContext(MyDbContext)
    MyDbContext.SaveChanges()
End Using

And here’s the TransactAbortContext class:

Imports System.Data.Entity.Infrastructure
Imports System.Data.Entity

''' <summary> 
''' Put a "SET XACT_ABORT ON" command on the connection for a dbcontext 
''' </summary> 
''' <remarks> 
''' This is necessary when calling updates on a synonym which is actually
''' a reference to a linked table on another SQL Server instance. 
''' </remarks> 
Public Class TransactAbortContext
    Implements IDisposable

    Private _dbContext As DbContext
    Private _disposed As Boolean ' To detect redundant calls 

    Public Sub New(ByVal dbContext As DbContext)
        Dim objectContext = DirectCast(dbContext, IObjectContextAdapter).ObjectContext
        objectContext.Connection.Open()
        objectContext.ExecuteStoreCommand("SET XACT_ABORT ON")
        _dbContext = dbContext
    End Sub

    Protected Overridable Sub Dispose(ByVal disposing As Boolean)
        If Not _disposed Then
            If disposing Then
                Dim objectContext = DirectCast(_dbContext, IObjectContextAdapter).ObjectContext
                objectContext.Connection.Close()
            End If
        End If
        _disposed = True
    End Sub

    Public Sub Dispose() Implements IDisposable.Dispose
        Dispose(True)
        GC.SuppressFinalize(Me)
    End Sub
End Class

A connection manually opened on the underlying ObjectContext is kept open and used for SaveChanges, until manually closed.

The TransactAbortContext class keeps track of this for the passed-in DbContext.