Frage

Ich versuche, die SNAPSHOT Transaktionsisolationsstufe in Microsoft SQL Server 2008 R2 mit dem Entity Framework 4.0 zu nutzen. Allerdings scheint dies nicht so einfach, wie ich zuerst gedacht zu sein.

Um die Verwendung SNAPSHOT-Isolationsstufe, hat es in der Datenbank aktiviert werden. Ich habe das getan. Und ich habe mithilfe von SQL Management Studio getestet, dass Snapshot-Isolationsstufe arbeitet als auf meiner Datenbank erwartet. Ich möchte diese Isolationsstufe verwenden, weil ich konsistent wollen liest, ohne die Zeilen oder die gesamte Tabelle zu sperren. Also meine Datenbank bereit ist für mich SNAPSHOT-Isolationsstufe zu verwenden. So weit so gut.

In meiner Repro-Anwendung, die eine WPF-Anwendung ist, habe ich ein Fenster, in dem ich einige Daten aus einer einzigen Tabelle laden. Ich lade 5 Zeilen gleichzeitig jedes Mal, wenn ich auf eine Schaltfläche klicken. Dies ist das XAML für das Fenster:

<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>

Und das ist der Code-Behind für das Fenster:

    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();
    }

Nichts geht magisch hier auf. Jetzt für den Code zu der Ansicht Modell, das ist, wo die Aktion geschieht.

    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));
        }
    }
}

Was ich versuche, hier zu tun, ist es, eine Verbindung zur Datenbank zu öffnen und offen zu halten. Ich versuche, eine Transaktion und fragen Sie nach der Snapshot-Isolationsstufe zu starten. Dies würde erlauben Sie mir 5 Reihen zu einem Zeitpunkt, zu lesen und die Reihen zu bekommen, wie sie waren, wenn das Fenster geöffnet wurde, auch wenn jemand würde zu bearbeiten, zu löschen oder Einfügungen, während das Fenster geöffnet ist. Aber wenn ich eine Spur mit SQL Profiler laufen, gibt es keine Transaktion gestartet wird, wenn das Fenster geöffnet wird oder wenn ich Zeilen laden, und die Isolationsstufe ich gefragt habe nicht gesetzt ist. Wenn das Fenster geöffnet wird, wird eine Verbindung geöffnet und Entity Framework stellt die Transaktion zu lesen Isolationsstufe ENGAGEMENT die die Standardisolationsstufe ist. Das gleiche geschieht (das heißt nichts), wenn ich eine Transaction anstelle eines DbTransaction verwenden.

Also meine Frage ist: Wie kann ich eine Transaktion mit Snapshot-Isolationsstufe beginnen und es so lange offen halten, wie mein Fenster geöffnet ist? Es ist unbedingt erforderlich, dass die Transaktion offen gehalten wird, so dass ich halten kann Daten aus dem Zusammenhang zu lesen, ohne Zeilen andere Benutzer des Lesen in der Zwischenzeit hinzugefügt.

Ich weiß, dass ich es mit rohen SQL-Befehle tun, aber ich möchte vermeiden, dass, wenn möglich.

Nebenbei bemerkt: Menschen unterschiedliche Meinungen bilden auf den verschiedenen Isolationsstufen haben, aber diese Frage ist nicht zu diskutieren, ob oder nicht SNAPSHOT-Isolationsstufe sachgemäßer ist in diesem Fall. SNAPSHOT arbeitet perfekt mit unseren Geschäftsanforderungen für diese Aufgabe. Die Frage wirklich über jede andere Isolationsstufe könnte auch, wie andere Isolationsstufen mit diesem Code funktioniert auch nicht.

War es hilfreich?

Lösung

Es tut mir leid, ich habe Ihre Zeit verschwendet. Der Code, den ich geschrieben tatsächlich funktioniert, zu meiner Überraschung. Getestet habe ich mein Programm von SQL Profiler und für eine „BEGIN TRANSACTION“ Anweisung und ein „SET TRANSACTION ISOLATION LEVEL SNAPSHOT“ aussehen. Es stellt sich jedoch heraus, dass Transaktionen zu verfolgen, müssen Sie speziell sie in der Ereignisliste in SQL Profiler wählen. Ich war nicht bewusst. Ich dachte, Transaktionen wie normale SQL-Befehle in Profiler verfolgt werden würde. Außerdem fand ich heraus, dass SQL Profiler nicht Änderungen in Transaktionsisolationsstufen verfolgen kann. Um herauszufinden, welche Transaktionsisolationsstufe eine Transaktion in, müssen Sie die sys.dm_exec_sessions Systemansicht abfragen. Es hat eine Spalte namens „transaction_isolation_level“, die einen numerischen Wert, der entspricht einem Isolationsstufe aufweist. Sie können für die Ansicht , was die Zahl bedeutet in der Dokumentation.

Als ich das erkennen, habe ich versucht, meinen Original-Code und abgefragt die Ansicht, und siehe da! Es war in der Tat in SNAPSHOT-Isolationsstufe.

Ich hoffe, das jemand retten kann sonst einige Zeit. : -)

Andere Tipps

Verwenden a TransactionOptions die Isolationsstufe des Systems Transaktionsbereich zu steuern:

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

Wenn nicht spezifiziert, wird die System.Transactions Serializable Isolationsstufe verwenden. Sie können auch eine Isolationsstufe von ReadCommitted verwenden, wenn Sie in der Datenbank READ_COMMITTED_SNAPSHOT aktiv sein.

Als allgemeine Regeln:

  • ist besser, eine Verbindung zu öffnen, nur für die Dauer einer Operation und schließen Sie es sofort. Connection Pooling wird es von dort.
  • ist absolut verboten, eine Transaktion für die Lebensdauer einer Form zu halten. Transaktion kann leben nur auf einem Stapel Umfang, für die Dauer einer bestimmten Operation (dh. Für einen Klick). Sonst wird Forgetful Fred seine Form offen lassen, und gehen Sie zum Mittagessen mit seiner anstehenden Transaktion die gesamte Datenbank Einfrieren.
scroll top