使用Entity Framework 4读取数据时如何使用事务?

Ren*_*ené 0 sql-server entity-framework transactions snapshot transaction-isolation

我正在尝试将Microsoft SQL Server 2008 R2中的SNAPSHOT事务隔离级别与Entity Framework 4.0一起使用.然而,这似乎并不像我最初想的那么容易.

要使用SNAPSHOT隔离级别,必须在数据库中启用它.我做到了.我已经使用SQL Management Studio测试了SNAPSHOT隔离级别在我的数据库上按预期工作.我想使用这个隔离级别,因为我想要一致的读取而不锁定行或整个表.所以我的数据库已准备好让我使用SNAPSHOT隔离级别.到现在为止还挺好.

在我的repro应用程序,这是一个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>
Run Code Online (Sandbox Code Playgroud)

这是窗口的代码隐藏:

    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();
    }
Run Code Online (Sandbox Code Playgroud)

没有什么神奇的事情发生在这里.现在为视图模型的代码,这是动作发生的地方.

    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));
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

我在这里要做的是打开与数据库的连接并保持打开状态.我尝试启动一个事务并询问SNAPSHOT隔离级别.这将允许我一次读取5行并获得打开窗口时的行,即使有人在窗口打开时编辑,删除或插入行.但是当我使用SQL事件探查器运行跟踪时,在窗口打开或加载行时没有启动事务,并且我要求的隔离级别没有设置.当窗口打开时,将打开一个连接,Entity Framework将事务隔离级别设置为READ COMMITTED,这是默认的隔离级别.如果我使用TransactionScope而不是DbTransaction,也会发生同样的事情(即什么都没有).

所以我的问题是:如果我的窗口打开,我怎么能用SNAPSHOT隔离级别启动事务并保持打开状态?事务保持打开是绝对必要的,这样我就可以继续从连接中读取数据,而无需读取其他用户平均添加的行.

我知道我可以使用原始SQL命令来完成它,但我想尽可能避免这种情况.

旁注:人们对不同的隔离级别有不同的看法,但这个问题不是为了讨论在这种情况下SNAPSHOT隔离级别是否合适.SNAPSHOT完全符合我们对此任务的业务需求.问题实际上可能与任何其他隔离级别有关,因为其他隔离级别对此代码也不起作用.

Ren*_*ené 6

对不起,我一直在浪费你的时间.我发布的代码实际上是令人惊讶的.我使用SQL Profiler测试了我的程序,并查找了"BEGIN TRANSACTION"语句和"SET TRANSACTION ISOLATION LEVEL SNAPSHOT".事实证明,要跟踪事务,您需要在SQL事件探查器的事件列表中专门选择它们.我没有意识到这一点.我认为事务将作为Profiler中的普通SQL命令进行跟踪.此外,我发现SQL事件探查器无法跟踪事务隔离级别的更改.要找出事务所在的事务隔离级别,您必须查询sys.dm_exec_sessions系统视图.它有一个名为"transaction_isolation_level"的列,其具有与隔离级别对应的数值.视图的文档.

当我意识到这一点时,我尝试了原始代码并查询了视图,不料!它确实处于SNAPSHOT隔离级别.

我希望这可以节省一些时间.:-)