Domanda

Sto cercando di utilizzare il livello di isolamento di transazione di snapshot in Microsoft SQL Server 2008 R2 con l'Entity Framework 4.0. Tuttavia, questo non sembra essere così facile come ho primo pensiero.

Per uso livello di isolamento SNAPSHOT, deve essere abilitato nel database. L'ho già fatto. E ho provato utilizzando SQL Management Studio che livello di isolamento SNAPSHOT funziona come previsto sul mio database. Voglio usare questo livello di isolamento perché voglio coerente legge senza bloccare le righe o l'intera tabella. Quindi il mio database è pronto per me di utilizzare il livello di isolamento SNAPSHOT. Fin qui tutto bene.

Nella mia applicazione Repro, che è un'applicazione WPF, ho una finestra in cui si carica un po 'di dati da una singola tabella. Mi carico 5 righe alla volta ogni volta che fa clic su un pulsante. Questo è il codice XAML per la finestra:

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

E questo è il code-behind per la finestra:

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

Niente magicamente succedendo qui. Ora, per il codice per il modello di vista, che è dove l'azione accade.

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

Quello che sto cercando di fare qui, è quello di aprire una connessione al database e tenerlo aperto. Io cerco di avviare una transazione e chiedere il livello di isolamento SNAPSHOT. Questo mi avrebbe permesso di leggere 5 righe alla volta e ottenere le righe come erano quando la finestra è stata aperta, anche se qualcuno avrebbe filari modificare, cancellare o inserire mentre la finestra è aperta. Ma quando ho eseguito una traccia con SQL Profiler, non c'è alcuna transazione in fase di avvio, quando si apre la finestra o quando carico le righe, e il livello di isolamento che ho chiesto non è stato impostato. Quando si apre la finestra, una connessione viene aperta, e Entity Framework imposta il livello di isolamento transazione Read Committed, che è il livello di isolamento predefinito. Lo stesso accade (cioè niente) se uso un TransactionScope invece di un DbTransaction.

Quindi la mia domanda è: Come posso avviare una transazione con il livello di isolamento snapshot e tenerlo aperto il più a lungo la mia finestra è aperta? E 'necessario assolutamente che l'operazione venga mantenuta aperta in modo da poter continuare a leggere i dati dalla connessione, senza leggere le righe di altri utenti è aggiunto nel frattempo.

So che posso farlo con i comandi SQL prime, ma vorrei evitare che, se possibile.

Nota a margine: La gente ha diverse oppinions sui diversi livelli di isolamento, ma questa domanda non è per discutere o meno di livello di isolamento snapshot è appropriata in questo caso. ISTANTANEA funziona perfettamente con il nostro requisito aziendale per questo compito. La questione potrebbe davvero essere di qualsiasi altro livello di isolamento e, come gli altri livelli di isolamento non funziona né con questo codice.

È stato utile?

Soluzione

Mi dispiace, ho sprecato il vostro tempo. Il codice che ho postato effettivamente funziona, con mia grande sorpresa. Ho testato il mio programma utilizzando SQL Profiler e cercato una dichiarazione "BEGIN TRANSACTION" e un "isolamento SET TRANSACTION LEVEL SNAPSHOT". Si scopre, però, che a monitorare le transazioni, è necessario selezionarli specificamente nella lista degli eventi in SQL Profiler. Non ero a conoscenza di questo. Ho pensato che le operazioni sarebbero monitorati come normali comandi SQL in Profiler. Inoltre, ho scoperto che SQL Profiler non può tracciare i cambiamenti nei livelli di isolamento delle transazioni. Per scoprire qual è il livello di isolamento delle transazioni una transazione è in, è necessario eseguire una query sulla vista di sistema sys.dm_exec_sessions. Ha una colonna denominata "transaction_isolation_level" che ha un valore numerico che corrisponde ad un livello di isolamento. Si può vedere ciò che il mezzo numero nella documentazione per la vista .

Quando ho capito questo, ho fatto del mio codice originale e interrogato la vista, ed ecco! Era infatti in livello di isolamento SNAPSHOT.

spero che questo può salvare qualcun altro po 'di tempo. : -)

Altri suggerimenti

Usa un TransactionOptions per controllare il livello di isolamento della portata dell'operazione di sistema:

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

Se lasciato non specificato, i System.Transactions utilizzerà livello di isolamento Serializable. È inoltre possibile utilizzare un livello di isolamento di ReadCommitted se è stata attivata READ_COMMITTED_SNAPSHOT nel database.

Come regole generali:

  • è meglio aprire una connessione solo per la durata un'operazione e chiuderla immediatamente. Il pool di connessioni sarà prendere da lì.
  • è assolutamente vietato tenere una transazione per la durata di un modulo. Transaction può vivere solo a parità di area di stack, per tutta la durata di un'operazione specifica (ad es. Per uno scatto del tasto). In caso contrario, Forgetful Fred lascerà la sua forma aperta e andare a pranzo, il congelamento l'intero database con la sua transazione in sospeso.
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top