Partager via


実例で学ぶアプリケーション開発 Ver.2 - サンプルアプリケーションのポイント(3)-TransactionとConcurrency

皆様、こんばんは!ソリューションサンプルの開発手順の開発の方は、まもなく、Part 3.から更新がされる予定ですが、少し先取りをしておきましょう。

ローカルトランザクション処理と分散トランザクション処理の実装

ソリューションサンプルでは OrderService 内でデータベースへのオーダーの作成とバスケットの削除をトランザクション処理として実装しています。これはWCFベースならではの機能の実装といえるでしょう。

トランザクション処理は、ローカルトランザクション、および分散トランザクションへの昇格を考慮した場合の2パターンを提供しています。以前のエントリでご紹介したサンプルコードではローカルトランザクションを利用しています。

そこで下記に、分散トランザクションへの昇格を考慮した場合のサンプルコードを掲載します。

※ 分散トランザクションへの昇格はパフォーマンス、およびデッドロックの可能性が高くなるため慎重に実装を検討してください。

 public void CreateOrder(string userId, int paymentType, Guid creaditCardId)
{
    using (MSStoreSampleEntities context = new MSStoreSampleEntities())
    {

        context.Connection.Open();

        using (TransactionScope scope = new TransactionScope())
        {
            int createOrderResult = context.sp_CreateOrder(userId, paymentType, creaditCardId);
            if (createOrderResult == 0)
            {
                throw new ApplicationException("オーダーを作成できませんでした。");
            }

            int deleteBasketResult = context.sp_DeleteBasket(userId);
            if (deleteBasketResult == 0)
            {
                throw new ApplicationException("バスケットを削除できませんでした。");
            }

            scope.Complete();
        }
    }
}

同時実行制御とエラー検出

次に、同時実行制御とエラ―検出について、ご紹介しましょう。このあたりは、Tech Days 2010でも軽くご紹介していますので、そちらに出て戴いた方には、復習となります。

① 同時実行制御の有効化

そもそも同時実行制御を有効にするには、まずはサーバー側:DomainService が利用している ADO.NET Entity Data Model のエンティティデザイナー上で、対象のエンティティを選択します。

image

選択したエンティティの同時実行制御の対象となるスカラプロパティを選択し、プロパティウィンドウで同時実行モードを “Fixed” に指定します。

image

クライアントアプリケーション:Silverlight側では、同時実行エラーの検出を、下記のように DomainContext クラスの SubmitChanges メソッドのコールバックメソッドを使って、下記のように実装します。

 private void SubmitOperationCompleted(SubmitOperation operation)
{
    if (operation.HasError)
    {
        if (operation.Error.GetType() == typeof(DomainOperationException))
        {
            DomainOperationException expception = (DomainOperationException)operation.Error;
            if (expception.Status == OperationErrorStatus.Conflicts)
            {
                foreach (var error in operation.EntitiesInError)
                {
                    if (error.EntityConflict != null)
                    {
                        // ローカルを優先する場合は Resolve() メソッドを呼び出す
                        error.EntityConflict.Resolve();
                    }
                }
            }
        }

        Dispatcher.BeginInvoke(() =>
        {
            ErrorWindow.CreateNew(operation.Error);
        });
        operation.MarkErrorAsHandled();
    }

    context.Load(context.GetBasketsByUserIdQuery(WebContext.Current.User.Name), BasketLoadOperationCompleted, null);
}

以上です。次回は、開発手順のPart 3.公開後に更新します。

鈴木 章太郎