问题描述
|
我正在尝试在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,false,out RearFileName,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,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.
谁能解释我做错了什么,或者为我指出正确做事的例子?
解决方法
希望您现在已经解决了此问题,但是如果其他任何人也遇到类似的问题:收到的错误消息表明您需要在
BD009
机器(可能是您自己的机器)上启动Distributed Transaction Coordinator服务。
,@Aasmund关于Distributed Transaction Coordinator
的答案可能会解决上述问题,但这仍然使您处于非理想状态:您正在将一笔交易锁定,该交易将ImagesStore
表(即使只是RowLock
)锁定为两个文件系统操作?并且您需要在该函数之外进行BEGIN
和COMMIT
事务(因为所提供的代码未处理该事务)。
我将这两部分分开:
步骤1:从表格中删除行
然后,如果没有出错,
步骤2:删除档案
在步骤1成功但随后步骤2由于任何原因失败的情况下,请执行以下一项或两项操作:
返回错误的状态代码,并在尝试删除状态表中的文件时跟踪哪个ѭ8错误。您可以使用它来手动删除文件和/或调试发生错误的原因。
创建一个可以定期运行以查找和删除未引用文件的进程。