1

Webアプリのエンティティコードに少しリンクがあります。基本的に、アプリがダウンロードされた回数をカウントします。私はこれが起こるかもしれないと心配しています:

  • セッション1はダウンロード数を読み取ります(例:50)
  • セッション2はダウンロード数を読み取ります(ここでも50)
  • セッション1はそれをインクリメントし、データベースに書き込みます(データベースストア51)
  • セッション2はそれをインクリメントし、データベースに書き込みます(データベースストア51)

これは私のコードです:

private void IncreaseHitCountDB()
{
    JTF.JTFContainer jtfdb = new JTF.JTFContainer();

    var app =
        (from a in jtfdb.Apps
         where a.Name.Equals(this.Title)
         select a).FirstOrDefault();

    if (app == null)
    {
        app = new JTF.App();
        app.Name = this.Title;
        app.DownloadCount = 1;

        jtfdb.AddToApps(app);
    }
    else
    {
        app.DownloadCount = app.DownloadCount + 1;
    }

    jtfdb.SaveChanges();
}

これが発生する可能性はありますか?どうすればそれを防ぐことができますか?

ありがとう、フィデル

4

3 に答える 3

2

Entity Frameworkは、デフォルトで楽観的同時実行モデルを使用します。Googleによると、楽観的とは「将来に希望と自信を持っている」という意味であり、まさにそれがEntityFrameworkの動作です。つまり、呼び出したときに、同時実行の問題が発生しないSaveChanges()ことを「希望と自信」があるため、変更を保存しようとするだけです。

Entity Frameworkが使用できるもう1つのモデルは、悲観的な同時実行モデルと呼ばれる必要があります(「可能な限り最悪の結果を期待する」)。このモードは、エンティティごとに有効にできます。あなたの場合、Appエンティティでそれを有効にします。これが私がすることです:

手順1.エンティティの同時実行チェックを有効にする

  1. .edmxファイルを右クリックし、[プログラムから開く... ]を選択します。
  2. ポップアップダイアログで[ XML(テキスト)エディター]を選択し、[OK]をクリックします。
  3. ConceptualModelsでAppエンティティを見つけます。アウトラインを切り替えて、必要に応じてタグを拡張することをお勧めします。あなたはこのようなものを探しています:

    <edmx:Edmx Version="2.0" xmlns:edmx="http://schemas.microsoft.com/ado/2008/10/edmx">
      <!-- EF Runtime content -->
      <edmx:Runtime>
        <!-- SSDL content -->
        ...
        <!-- CSDL content -->
        <edmx:ConceptualModels>
          <Schema Namespace="YourModel" Alias="Self" xmlns:annotation="http://schemas.microsoft.com/ado/2009/02/edm/annotation" xmlns="http://schemas.microsoft.com/ado/2008/09/edm">
            <EntityType Name="App">
    
  4. EntityTypeの下に、一連の<Property>タグが表示されます。存在する場合Name="Status"は、を追加して変更しConcurrencyMode="Fixed"ます。プロパティが存在しない場合は、次の場所にコピーします。

    <Property Name="Status" Type="Byte" Nullable="false" ConcurrencyMode="Fixed" />
    
  5. ファイルを保存し、 .edmxファイルをダブルクリックしてデザイナビューに戻ります。

ステップ2.呼び出し時の並行性の処理SaveChanges()

SaveChanges()2つの例外のうちの1つをスローします。おなじみのUpdateExceptionまたはOptimisticConcurrencyException

設定されているエンティティに変更を加えた場合ConcurrencyMode="Fixed"、EntityFrameworkは最初にデータストアに変更がないかチェックします。変更がある場合は、OptimisticConcurrencyExceptionがスローされます。変更がない場合は、通常どおり続行されます。

キャッチしたら、のRefresh()メソッドOptimisticConcurrencyExceptionを呼び出して、再試行する前に計算をやり直す必要があります。エンティティを更新するための呼び出しは、データストア内のデータを使用して競合が解決されることを意味します。同時に変更されるDownloadCountは競合です。ObjectContextRefresh()RefreshMode.StoreWins

これがあなたのコードをどのように見せるかです。これは、エンティティの取得と呼び出しの間に多くの操作がある場合に便利であることに注意してくださいSaveChanges()

    private void IncreaseHitCountDB()
    {
        JTF.JTFContainer jtfdb = new JTF.JTFContainer();

        var app =
            (from a in jtfdb.Apps
             where a.Name.Equals(this.Title)
             select a).FirstOrDefault();

        if (app == null)
        {
            app = new JTF.App();
            app.Name = this.Title;
            app.DownloadCount = 1;

            jtfdb.AddToApps(app);
        }
        else
        {
            app.DownloadCount = app.DownloadCount + 1;
        }

        try
        {
            try
            {
                jtfdb.SaveChanges();
            }
            catch (OptimisticConcurrencyException)
            {
                jtfdb.Refresh(RefreshMode.StoreWins, app);
                app.DownloadCount = app.DownloadCount + 1;
                jtfdb.SaveChanges();
            }
        }
        catch (UpdateException uex)
        {
            // Something else went wrong...
        }
    }
于 2012-05-11T02:58:35.603 に答える
1

インクリメントする直前にダウンロードカウント列をクエリするだけで、これが発生するのを防ぐことができます。読み取りとインクリメントの間にかかる時間が長くなると、別のセッションがそれを読み取る必要がある時間が長くなります(後で書き換え-誤って-インクリメントされます)数)、したがって、カウントを台無しにします。

単一のSQLクエリで:

UPDATE Data SET Counter = (Counter+1)

Linq To Entitiesなので、実行の遅延を意味します。別のセッションでカウントを台無しにする(同じベースをインクリメントし、そこで1カウントを失う)には、アプリをインクリメントする必要があります。

    else
    {
        app.DownloadCount += 1; //First line
    }

    jtfdb.SaveChanges();  //Second line
}

つまり、変更が発生するためのウィンドウが非常に小さいため、前のカウントが古くなり、このようなアプリケーションでは事実上不可能です。

Im no LINQ proなので、LINQが実際にapp.DownLoadCountを取得してから追加するのか、SQLコマンドで追加するだけなのかわかりませんが、どちらの場合も、そのimhoについて心配する必要はありません。

于 2011-09-06T11:46:17.977 に答える
0

このシナリオで何が起こるかを簡単にテストできます。スレッドを開始し、スリープ状態にしてから、別のスレッドを開始します。

else
{
    app.DownloadCount = app.DownloadCount + 1;
}

System.Threading.Thread.Sleep(10000);
jtfdb.SaveChanges();

しかし、簡単な答えは、いいえ、Entity Frameworkはデフォルトで同時実行チェックを実行しないということです(MSDN-変更の保存と同時実行の管理)

そのサイトはあなたにいくつかの背景を提供します。

あなたのオプションは

  • 同時実行チェックを有効にします。つまり、2人のユーザーが同時にダウンロードし、2番目のユーザーが読み取った後、2番目のユーザーが更新する前に最初の更新を行うと、例外が発生します。
  • テーブル内の値を直接インクリメントするストアドプロシージャを作成し、1回の操作でコードからストアドプロシージャを呼び出しますIncrementDownloadCounter。これにより、「読み取り」がなくなり、「ダーティ読み取り」の可能性がなくなります。
于 2011-09-06T13:51:28.317 に答える