在当前事务中登记的SQL Server CLR集成

时间:2011-05-26 15:10:23

标签: sql-server transactions sqlclr

我正在尝试在SQL Server中使用CLR集成来处理访问外部文件,而不是将它们作为BLOB存储在内部。我正在试图找出我需要遵循的模式,以使我的代码在当前的SQL事务中登记。我想我会从最简单的场景开始,删除现有的行,因为插入/更新场景会更复杂。

[SqlProcedure]
public static void DeleteStoredImages(SqlInt64 DocumentID)
{
    if (DocumentID.IsNull)
        return;

    using (var conn = new SqlConnection("context connection=true"))
    {
        conn.Open();

        string FaceFileName, RearFileName;
        int Offset, Length;
        GetFileLocation(conn, DocumentID.Value, true,
            out FaceFileName, out Offset, out Length);
        GetFileLocation(conn, DocumentID.Value, false,
            out RearFileName, out Offset, out Length);

        new DeleteTransaction().Enlist(FaceFileName, RearFileName);

        using (var comm = conn.CreateCommand())
        {
            comm.CommandText = "DELETE FROM ImagesStore WHERE DocumentID = " + DocumentID.Value;
            comm.ExecuteNonQuery();
        }
    }
}

private class DeleteTransaction : IEnlistmentNotification
{
    public string FaceFileName { get; set; }
    public string RearFileName { get; set; }
    public void Enlist(string FaceFileName, string RearFileName)
    {
        this.FaceFileName = FaceFileName;
        this.RearFileName = RearFileName;
        var trans = Transaction.Current;
        if (trans == null)
            Commit(null);
        else
            trans.EnlistVolatile(this, EnlistmentOptions.None);
    }
    public void Commit(Enlistment enlistment)
    {
        if (FaceFileName != null && File.Exists(FaceFileName))
        {
            File.Delete(FaceFileName);
        }
        if (RearFileName != null && File.Exists(RearFileName))
        {
            File.Delete(RearFileName);
        }
    }

    public void InDoubt(Enlistment enlistment)
    {
    }

    public void Prepare(PreparingEnlistment preparingEnlistment)
    {
        preparingEnlistment.Prepared();
    }

    public void Rollback(Enlistment enlistment)
    {
    }
}

当我真的试图运行它时,我得到以下异常:

A .NET Framework error occurred during execution of user defined routine or aggregate 'DeleteStoredImages': 
System.Transactions.TransactionException: The operation is not valid for the state of the transaction. ---> System.Transactions.TransactionPromotionException: MSDTC on server 'BD009' is unavailable. ---> System.Data.SqlClient.SqlException: MSDTC on server 'BD009' is unavailable.
System.Data.SqlClient.SqlException: 
   at System.Data.SqlServer.Internal.StandardEventSink.HandleErrors()
   at System.Data.SqlServer.Internal.ClrLevelContext.SuperiorTransaction.Promote()
System.Transactions.TransactionPromotionException: 
   at System.Data.SqlServer.Internal.ClrLevelContext.SuperiorTransaction.Promote()
   at System.Transactions.TransactionStatePSPEOperation.PSPEPromote(InternalTransaction tx)
   at System.Transactions.TransactionStateDelegatedBase.EnterState(InternalTransaction tx)
System.Transactions.TransactionException: 
   at System.Transactions.TransactionState.EnlistVolatile(InternalTransaction tx, IEnlistmentNotification enlistmentNotification, EnlistmentOptions enlistmentOptions, Transaction atomicTransaction)
   at System.Transactions.TransactionStateSubordinateActive.EnlistVolatile(InternalTransaction tx, IEnlistmentNotification enlistmentNotification, EnlistmentOptions enlistmentOptions, Transaction atomicTransaction)
   at System.Transactions.Transaction.EnlistVolatile(IEnlistmentNotification enlistmentNotification, EnlistmentOptions enlistmentOptions)
   at ExternalImages.StoredProcedures.DeleteTransaction.Enlist(String FaceFileName, String RearFileName)
   at ExternalImages.StoredProcedures.DeleteStoredImages(SqlInt64 DocumentID)
. User transaction, if any, will be rolled back.
The statement has been terminated.

任何人都可以解释我做错了什么,或者给我一个如何正确做法的例子?

2 个答案:

答案 0 :(得分:0)

您希望现在已经解决了这个问题,但是如果其他人遇到类似的问题:您收到的错误消息表明您需要在{{1}上启动分布式事务处理协调器服务机器(可能是你自己的机器)。

答案 1 :(得分:0)

@ Aasmund关于Distributed Transaction Coordinator的答案可能会解决所述问题,但这仍然会使您处于非理想状态:您正在绑定一个事务,该事务会锁定ImagesStore表(即使它两个文件系统操作只是一个RowLock)?并且您需要BEGINCOMMIT此函数之外的事务(因为这不会在显示的代码中处理)。

我将这两件作品分开:

  • 第1步:删除表中的行

    然后,如果没有错误,

  • 第2步:删除文件

在步骤1成功但后续步骤2的情况下,无论出于何种原因,失败,请执行以下一项或两项操作:

  • 返回错误状态代码,并在尝试删除状态表中的文件时跟踪哪些DocumentID出错。您可以使用它来手动删除文件和/或调试错误发生的原因。

  • 创建一个可定期运行以查找和删除未引用文件的流程。