9

トランザクションとmsdtcがどのように連携するかについて基本的な混乱があります。

基本的なサーバー/クライアントのwinformsアプリがあります。アプリはtransactionscopeを使用して、SQLサーバーで実行されるいくつかのSQLコマンドをカプセル化します。

サーバーでのみmsdtcネットワークアクセスを有効にした場合、アプリは正常に動作しているように見えました。その後、ある日、ネットワークアクセスが有効になっていないと言って動作を停止しました。

トランザクションスコープを機能させるには、クライアントコンピューターとサーバーの両方でmsdtcネットワークアクセスを有効にする必要があるようです。

クライアントまたはサーバーのmsdtcサービスはトランザクションを機能させますか?または多分その両方?

msdtcネットワークアクセスがクライアントとサーバーの両方で必要か、それともサーバーだけで必要かについてのガイダンスはありますか?

4

3 に答える 3

11

MSDTCを使用している場合は、クライアント(アプリケーション)とサーバー(データベース)の両方でMSDTCを実行し、適切に構成する必要があります。

これは、特にファイアウォールを扱うときに苦痛の原因となる可能性があります。問題が発生した場合は、MSDTCの問題のトラブルシューティングを参照してください。BizTalkについて説明していますが、一般的にMSDTCに適用されます。 DTCPINGもあなたの友達です。

SQL Server 2005以降を使用していて、1つのデータベースにのみアクセスし、1つのデータベース接続を使用していて、アプリドメイン間でトランザクションを渡していない場合は、MSDTCを使用する必要はありません。そのような状況では、System.Transactionsトランザクションマネージャーがトランザクションを管理します。上記の状況のいずれかが発生した場合、トランザクションは分散トランザクションにプロモートされます(トランザクションマネージャーはMSDTCになります)。詳細については、トランザクション管理のエスカレーションを参照してください。

一般に、MSDTCが必要ない場合は、MSDTCの使用を避けるのが最善です。つまり、単一のSQL Server 2005+データベースのみを扱っている場合は、MSDTCを使用しないようにコードを設計してみてください。構成の煩わしさに加えて、MSDTCへのすべての呼び出しが2フェーズコミットプロトコル(MSDTCが使用する)のオーバーヘッドと組み合わされてプロセス外であるため、DTCはパフォーマンスの低下を課します。

あなたの特定の状況で何が起こっているかという点では、言うのは難しいです。コードが変更されていない場合は、ファイアウォールルールが変更されている可能性がありますか?また、Windows Updateが(セキュリティのために)DTC構成を変更し、問題が発生するのを見ました。

コメントに基づく更新:

トランザクションの昇格またはエスカレーションを監視するために、分散トランザクションを使用していない場合は、分散トランザクションコーディネーターのパフォーマンスカウンターの一部を使用して、コミットされたトランザクションを追跡できると思います。テストする場合は、MSDTCを無効にして、コードが失敗するかどうかを確認できます。もう1つの方法は、SQLServerでトランザクションを監視することです。コーディングの観点からは、DistributedTransactionStartedイベントを処理し、ログを記録することができます(ただし、本番環境に移行する前にそのコードを削除してください)。

単一の接続を使用するコード例については、MSDNのTransactionScopeページにアクセスしてください。基本的に、TransactionScopeを作成し、SqlConnectionを作成し、SqlConnectionを操作して、接続を閉じ、scope.Complete()を呼び出します。

データアダプタメソッドを使用している場合は、接続が自動的に管理されるため、接続が閉じられるか、接続プールに戻されることに注意してください。いずれにせよ、別の操作が呼び出されると、トランザクションはDTCトランザクションにプロモートされます。詳細については、System.Transactionsと接続プールを参照してください。

于 2009-10-14T06:39:22.013 に答える
8

@Tuzoの説明を拡張するために、常にエスカレーションするコマンドの例を次に示します。

using(var scope = new TransactionScope())
{
  using(var conn = new SqlConnection(connString)){
     conn.Open();
     //...some command, etc.
  }
  using(var conn = new SqlConnection(connString)){
     conn.Open();
     //...some command, etc.
  }
  scope.Complete();
}

実際には、接続とコマンドは別のクラスなどにありますが、あなたはその考えを理解します。同じデータベースへの接続文字列であっても、2つの接続であるため、DTCを使用してエスカレーションします。エスカレーションしないトランザクションは次のようになります。

using(var scope = new TransactionScope())
{
  using(var conn = new SqlConnection(connString)){
     conn.Open();
     //...some command, etc.
     //...some other command, etc.
  }
  scope.Complete();
}

接続を開き、必要なことを実行し、できるだけ早く閉じるため、これはとにかくより良いコードです。これは、接続管理について考える必要があることを意味します。アプリによっては、これを異なる方法で実装する場合があります。例えば:

using(var scope = new TransactionScope())
using(var conn = new SqlConnection(connString))
{
    conn.Open();
    var myService = new MyService(conn);
    var myService2 = new MyService2(conn);
    myService.DoSomething();
    myService2.DoSomething();
    scope.Complete();
}

これを実装するにはさまざまな方法があります。エンタープライズライブラリデータアクセスアプリケーションブロック、およびさまざまなORMも、接続とトランザクションをより効率的に処理するのに役立つ場合があります。

于 2009-10-14T14:11:39.980 に答える
0

更新:回避策とともに、TransactionScope内の同じデータアダプターでGetDataとUpdateの両方を使用する場合にのみ、トランザクションがLTMからMSDTCにプロモートされる理由を説明する記事を見つけました。

決定的なTableAdapters+Transactionsブログ投稿 http://blah.winsmarts.com/2006/06/18/the-definitive-tableadapters--transactions-blog-post.aspx

一度に複数の接続を開いて、分散するトランザクションをエスカレートすることについての部分を理解しています。ただし、接続が1つだけで、それをエスカレートしているデータベースに対するクエリが1つしかないという問題があります。ストアドプロシージャにもトランザクションはありません。手がかりがあれば聞いてみたいです。私のコード例から、「adapter.Update(table)」は分散トランザクションをトリガーします。

私は既存のプロジェクトからコードの内臓を取り除き、進行中のほとんどを単純化しましたが、それでも同じ問題が発生しています。これは基本的に、テーブルアダプタを使用してデータセットを作成し、選択、挿入、および削除するためのストアドプロシージャを使用してデータセットを設定することです。特定のユーザーに関連するすべてのレコードを選択します。次に、レコードの1つに「myPPID」が存在するかどうかに応じて、それを追加または削除します。次に、updateメソッドを呼び出し、コンポーネントサービスのトランザクション統計を監視して、トランザクションが分散されるようにエスカレートすることを確認します。

クライアントプログラムにWindowsXPProSP3と.NetFramework3.5を使用しています。LAN経由でSQL2005データベースに接続し、Windows Server 2003 R2 EnterpriseEditionSP2に接続します。

private void button1_Click(object sender, EventArgs e)
{
int userId = 3;
int myPPId = 881;
using (TransactionScope ts = new TransactionScope())
{
    using (DataSet1TableAdapters.AssignedPPTableAdapter adapter 
    = new MSDTCPromotionTest.DataSet1TableAdapters.AssignedPPTableAdapter())
    {
        using (DataSet1.AssignedPPDataTable table = adapter.GetData(userId))
        {
            DataSet1.AssignedPPRow row = table.FindByUserIdmyPPId(
                userId, myPPId);
            if (row == null)
            {
                table.AddAssignedPPRow(userId, myPPId, string.Empty, 
                    string.Empty, true);
            }
            else
            {
                row.Delete();
            }
            adapter.Update(table);
        }
        ts.Complete();
    }
}
}

接続文字列は特別なものではありません。

<add name="ConnectionString" connectionString="
Data Source=devdb;
Initial Catalog=&quot;TEST MSDTC&quot;;
Integrated Security=True"
providerName="System.Data.SqlClient" />

また、ストアドプロシージャは単純なクラッド呼び出しです。

作成:

ALTER procedure [dbo].[p_UserForm_AssignedPP_Insert]
(
    @UserId INT,
    @myPPId int
)
AS
SET NOCOUNT ON;
INSERT INTO [UsermyPP] ([UserID],[myPPID],[DateCreated])
     VALUES (@UserId,@myPPId,GETutcDATE()) 

読む:

ALTER procedure [dbo].[p_UserForm_AssignedPP_SelectByUserId]
(
    @UserId int
)
AS
SELECT  
    [UserId],
    [myPPId], 
    '' Title,
    '' Abbreviation,
    0 IsArchived
from
    UsermyPP  unpp
where
    unpp.[userid] = @UserId

消去:

ALTER procedure [dbo].[p_UserForm_AssignedPP_Delete]
(
    @Original_UserId INT,
    @Original_MyPPId INT
)
AS
SET NOCOUNT ON;
DELETE FROM usermypp WHERE [UserID] = @Original_UserId 
    AND [MyPPID] = @Original_MyPPId
于 2010-05-14T16:27:11.577 に答える