要使用快照隔离级别,必须在数据库中启用它。我做到了。我使用SQL Management Studio测试了快照隔离级别在我的数据库上的工作情况。我想使用这个隔离级别,因为我希望在不锁定行或整个表的情况下进行一致的读取。所以我的数据库已经准备好使用快照隔离级别了。到现在为止,一直都还不错。
<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));
}
}
}
我在这里要做的是打开一个到数据库的连接并保持它打开。我尝试启动一个事务并请求快照隔离级别。这将允许我一次读取5行,并获得窗口打开时的行,即使有人会在窗口打开时编辑、删除或插入行。但是,当我使用SQL Profiler运行跟踪时,在窗口打开或加载行时不会启动任何事务,并且不会设置我要求的隔离级别。当窗口打开时,连接打开,实体框架将事务隔离级别设置为READ COMMITTED,这是默认的隔离级别。如果我使用TransactionScope而不是DbTransaction,也会发生同样的情况(即什么都没有)。
因此,我的问题是:如何启动具有快照隔离级别的事务,并在窗口打开的情况下保持其打开状态?事务保持打开是绝对必要的,这样我就可以继续从连接中读取数据,而不必同时读取其他用户添加的行。
我知道我可以用原始的SQL命令来完成,但如果可能的话,我想避免这样做。
旁注:人们在不同的隔离级别上有不同的操作,但这个问题不是为了讨论在这种情况下快照隔离级别是否合适。快照与此任务的业务需求完美配合。问题也可能是关于任何其他隔离级别,因为其他隔离级别也不适用于此代码。