2011-05-26 145 views
0

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

[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. 

任何人都可以解释我做错了,或点我怎么做是正确的一个例子?

回答

0

你希望解决这个现在,但如果任何人有类似的问题:你所得到的错误信息显示您需要启动BD009机器上分布式事务处理协调服务(大概是你自己的机器)。

+0

谢谢,其实我结束了,现在放弃了整个事情,因为各种问题(这是只是其中之一),但如果我重新审视这个问题,知道关于分布式事务处理协调器可以提供一个有用的出发地点。 – 2011-06-28 15:10:04

0

@ Aasmund的关于Distributed Transaction Coordinator答案可能解决了上述问题,但仍让你在非理想状态:你是捆绑交易,其中锁定ImagesStore表(即使它只是一个RowLock),以两个文件系统操作?并且您需要BEGINCOMMIT该函数之外的事务(因为在所提供的代码中未处理该事务)。

我会分离这两个部分:

  • 步骤1:从表中删除该行

    ,然后,如果没有出错,

  • 步骤2:删除文件

在步骤1成功但步骤2的情况下,对于wha tever原因而失败,请执行下列操作之一或全部:

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

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