0

Microsoft SQL Server2008R2のSNAPSHOTトランザクション分離レベルをEntityFramework4.0で利用しようとしています。しかし、これは私が最初に思ったほど簡単ではないようです。

SNAPSHOT分離レベルを使用するには、データベースで有効にする必要があります。私はそれをしました。また、SQL Management Studioを使用して、SNAPSHOT分離レベルがデータベースで期待どおりに機能することをテストしました。行またはテーブル全体をロックせずに一貫した読み取りが必要なため、この分離レベルを使用したいと思います。これで、データベースでSNAPSHOT分離レベルを使用できるようになりました。ここまでは順調ですね。

WPFアプリケーションである私の再現アプリケーションには、単一のテーブルからいくつかのデータをロードするウィンドウがあります。ボタンをクリックするたびに一度に5行をロードします。これはウィンドウのXAMLです。

<Window x:Class="EFSnapshotTransactionTest.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="MainWindow" Height="350" Width="525" Name="UC" Closing="UC_Closing">
<DockPanel>
    <Button Click="Button_Click" DockPanel.Dock="Top">Load next 5</Button>
    <ScrollViewer>
        <ListView ItemsSource="{Binding ElementName=UC, Path=ViewModel.Items}">
            <ListView.View>
                <GridView>
                    <GridViewColumn Header="Id" DisplayMemberBinding="{Binding Id}"/>
                    <GridViewColumn Header="Date" DisplayMemberBinding="{Binding Date}"/>
                    <GridViewColumn Header="DocumentNumber" DisplayMemberBinding="{Binding DocumentNumber}"/>
                    <GridViewColumn Header="Amount" DisplayMemberBinding="{Binding Amount}"/>
                    <GridViewColumn Header="Text" DisplayMemberBinding="{Binding Text}"/>
                </GridView>
            </ListView.View>
        </ListView>
    </ScrollViewer>
</DockPanel>

そして、これはウィンドウのコードビハインドです:

    public partial class MainWindow : Window
{
    private ViewModel _vm;

    public ViewModel ViewModel
    {
        get { return _vm; }
    }

    public MainWindow()
    {
        _vm = new ViewModel();
        InitializeComponent();
    }

    private void Button_Click(object sender, RoutedEventArgs e)
    {
        _vm.LoadNextItems(5);
    }

    private void UC_Closing(object sender, System.ComponentModel.CancelEventArgs e)
    {
        _vm.Dispose();
    }

ここでは魔法のように何も起こっていません。次に、アクションが発生するビューモデルへのコードについて説明します。

    public class ViewModel : INotifyPropertyChanged, IDisposable
{
    private ObservableCollection<Posting> _items;
    private SentaFinancialsEntities _db;
    private DbTransaction _dbTrans;

    public ObservableCollection<Posting> Items
    {
        get { return _items; }
        set
        {
            _items = value;
            OnPropertyChanged("Items");
        }
    }

    public ViewModel()
    {
        _items = new ObservableCollection<Posting>();
        _db = new SentaFinancialsEntities();
        _db.Connection.Open();
        _dbTrans = _db.Connection.BeginTransaction(System.Data.IsolationLevel.Snapshot);
    }

    public void LoadNextItems(int count)
    {
        int startAt = _items.Count;
        var dbPostings = (from b in _db.Postings
                          select b).OrderBy(b => b.Dato).Skip(startAt).Take(count);
        foreach (var singleDbPosting in dbPostings)
        {
            Posting dto = new Posting(singleDbPosting);
            _items.Add(dto);
        }
    }

    public void Dispose()
    {
        _dbTrans.Commit();
        _dbTrans.Dispose();
        _db.Dispose();
    }

    public event PropertyChangedEventHandler PropertyChanged;

    private void OnPropertyChanged(string propertyName)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

私がここでやろうとしているのは、データベースへの接続を開き、それを開いたままにすることです。トランザクションを開始して、SNAPSHOT分離レベルを要求しようとしています。これにより、ウィンドウが開いているときに誰かが行を編集、削除、または挿入した場合でも、一度に5行を読み取り、ウィンドウを開いたときの行を取得できます。しかし、SQL Profilerを使用してトレースを実行すると、ウィンドウが開いたとき、または行をロードしたときにトランザクションが開始されず、要求した分離レベルが設定されていません。ウィンドウが開くと、接続が開かれ、EntityFrameworkはトランザクション分離レベルをデフォルトの分離レベルであるREADCOMMITTEDに設定します。DbTransactionの代わりにTransactionScopeを使用した場合も、同じことが起こります(つまり何も起こりません)。

だから私の質問は:SNAPSHOT分離レベルでトランザクションを開始し、ウィンドウが開いている限りそれを開いたままにするにはどうすればよいですか?他のユーザーが追加した行を読み取らずに、接続からデータを読み取り続けることができるように、トランザクションを開いたままにしておくことが絶対に必要です。

生のSQLコマンドでそれができることは知っていますが、可能であればそれを避けたいと思います。

補足:人々はさまざまな分離レベルでさまざまな意見を持っていますが、この質問は、この場合にSNAPSHOT分離レベルが適切であるかどうかを議論するためのものではありません。SNAPSHOTは、このタスクのビジネス要件と完全に連携します。他の分離レベルもこのコードでは機能しないため、問題は他の分離レベルについても同様である可能性があります。

4

2 に答える 2

6

申し訳ありませんが、私はあなたの時間を無駄にしてきました。驚いたことに、私が投稿したコードは実際に機能します。SQLプロファイラーを使用してプログラムをテストし、「BEGINTRANSACTION」ステートメントと「SETTRANSACTIONISOLATIONLEVELSNAPSHOT」を探しました。ただし、トランザクションを追跡するには、SQLプロファイラーのイベントリストでトランザクションを具体的に選択する必要があります。私はそれを知りませんでした。トランザクションは、プロファイラーでは通常のSQLコマンドとして追跡されると思いました。さらに、SQLプロファイラーはトランザクション分離レベルの変更を追跡できないことがわかりました。トランザクションがどのトランザクション分離レベルにあるかを確認するには、sys.dm_exec_sessionsシステムビューにクエリを実行する必要があります。分離レベルに対応する数値を持つ「transaction_isolation_level」という列があります。ビューのドキュメント

これに気付いたとき、私は自分の元のコードを試し、ビューを照会しました、そして見よ!それは確かにSNAPSHOT分離レベルでした。

これで他の人の時間を節約できることを願っています。:-)

于 2010-11-11T11:24:31.820 に答える
2

a を使用しTransactionOptionsて、システム トランザクション スコープの分離レベルを制御します。

var TransactionOptions to = new TransactionOptions () 
 { IsolationLevel = IsolationLevel.Snapshot};
using (TransactionScope scope = new TransactionScope(
    TransactionScope.Required, to))
{
   // Do the work here
   ...
   scope.Complete ();
}

指定しない場合、System.Transactions はSerializable分離レベルを使用します。ReadCommittedデータベースで read_committed_snapshot を有効にした場合は、分離レベルを使用することもできます。

一般的な規則として:

  • 操作中だけ接続を開き、すぐに閉じることをお勧めします。接続プーリングはそこから取得します。
  • フォームの存続期間中にトランザクションを保持することは絶対に禁止されています。トランザクションは、特定の操作の間 (つまり、1 回のボタン クリックの間)、スタック スコープでのみ存在できます。そうしないと、忘却のフレッドはフォームを開いたまま昼食に行き、保留中のトランザクションでデータベース全体を凍結します。
于 2010-11-10T18:47:07.627 に答える