Интеграция SQL Server CLR с участием в текущей транзакции - PullRequest
0 голосов
/ 26 мая 2011

Я пытаюсь использовать интеграцию CLR в SQL Server для обработки доступа к внешним файлам вместо того, чтобы хранить их внутри как 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 голосов
/ 01 февраля 2015

@ Ответ Aasmund относительно Distributed Transaction Coordinator может решить указанную проблему, но это все еще оставляет вас в неидеальном состоянии: вы связываете транзакцию, которая блокирует таблицу ImagesStore (даже если это просто RowLock), до двух операций файловой системы? И вам нужно BEGIN и COMMIT транзакции вне этой функции (так как это не обрабатывается в представленном коде).

Я бы разделил эти две части:

  • Шаг 1: Удалить строку из таблицы

    , а затем, если это не ошибка,

  • Шаг 2: Удалить файл (ы)

В сценарии, где Шаг 1 успешен, но затем Шаг 2, по любой причине, терпит неудачу, выполните одно или оба из следующих действий:

  • возвращает код состояния ошибки и отслеживает, какие DocumentID s получили ошибку при попытке удалить файл в таблице состояния. Вы можете использовать это, чтобы вручную удалить файлы и / или отладить причину ошибки.

  • создать процесс, который может периодически запускаться для поиска и удаления файлов, на которые нет ссылок.

0 голосов
/ 23 июня 2011

Мы надеемся, что вы уже решили эту проблему, но в случае, если у кого-то еще есть подобная проблема: полученное сообщение об ошибке указывает на необходимость запуска службы Координатор распределенных транзакций на компьютере BD009(предположительно ваша собственная машина).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...