Question

I am porting a set of T-SQL stored procedures to C#/CLR. In the T-SQL, we use XACT_STATE() to determine if an error requires rolling back to a savepoint or rolling back the transaction entirely.

I cannot find an equivalent check of the transaction using the System.Data.SqlClient.SqlTransaction or System.Transactions.TransactionScope objects.

Does anyone know how to check the transaction state from within C#/CLR?

Was it helpful?

Solution

XACT_STATE() is not something that SqlClient has any special insight on, much like transaction level. So, just query it with SqlCommand.

// If you already have an open connection...
public int GetXactState (SqlConnection connection)
{   
    using (SqlCommand command = new SqlCommand("Select XACT_STATE();", connection)) {
        return (int)command.ExecuteScalar();
    }
}

OTHER TIPS

Yes you can. However, it depends on the internals of the System.Data.SqlClient assembly and so I would not recommend it. If it is truly required I would reassess if CLR is actually the correct way to do what you are trying to do.

The SqlTransaction is actually only a wrapper class around the SqlInternalTransaction class. The SqlInternalTransaction class already tracks the state of the transaction for you (if it didn't then how would the SqlClient know whether to rollback or not?)

You will need to use reflection to obtain the instance of the SqlInternalTransaction which again I would not recommend doing in a CLR procedure as this can be pretty costly in terms of performance - especially if it gets called many times.

That being said, here is how you do it.

public enum TransactionState
{
    Pending = 0,
    Active = 1,
    Aborted = 2,
    Committed = 3,
    Unknown = 4,
}

public static class SqlExtensions
{
    public static TransactionState GetXactState(this SqlTransaction transaction)
    {
        BindingFlags bindingFlags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance;

        PropertyInfo property = transaction.GetType().GetProperties(bindingFlags).FirstOrDefault(x => x.Name.Equals("InternalTransaction", StringComparison.OrdinalIgnoreCase);
        if (property == null)
            return TransactionState.Unknown;

        object internalTransaction = property.GetValue(transaction);

        FieldInfo transactionState = property.PropertyType.GetField("_transactionState", bindingFlags);
        if (property == null)
            return TransactionState.Unknown;

        return (TransactionState)Enum.Parse(typeof(TransactionState), Convert.ToString(transactionState.GetValue(internalTransaction)), true);
    }
}

class Program
{
    static void Main(string[] args)
    {
        SqlTransaction x = null;

        // use transaction etc.. here.

        TransactionState state = x.GetXactState();
    }
}
Licensed under: CC-BY-SA with attribution
Not affiliated with dba.stackexchange
scroll top