首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >在Entity Framework4中读取数据时如何使用事务?

在Entity Framework4中读取数据时如何使用事务?
EN

Stack Overflow用户
提问于 2010-11-11 00:13:00
回答 2查看 2.8K关注 0票数 0

我正在尝试利用Microsoft SQL Server2008 R2和Entity Framework4.0中的快照事务隔离级别。然而,这似乎并不像我最初想象的那么容易。

要使用快照隔离级别,必须在数据库中启用快照隔离级别。我已经做过了。我已经使用SQL Management Studio测试了该快照隔离级别在我的数据库上的预期效果。我希望使用此隔离级别,因为我希望在不锁定行或整个表的情况下保持一致的读取。因此,我的数据库已准备好使用快照隔离级别。到目前一切尚好。

在我的repro应用程序中,这是一个WPF应用程序,我有一个窗口,在这个窗口中,我从一个表中加载了一些数据。每次单击按钮时,我一次加载5行。这是窗口的XAML:

代码语言:javascript
复制
<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>

这是该窗口的代码隐藏:

代码语言:javascript
复制
    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();
    }

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

代码语言:javascript
复制
    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事件探查器运行跟踪时,在打开窗口或加载行时没有启动任何事务,并且没有设置我要求的隔离级别。当窗口打开时,将打开一个连接,并且实体框架将事务隔离级别设置为“读已提交”,这是默认的隔离级别。如果我使用TransactionScope而不是DbTransaction,也会发生同样的情况(即没有发生任何事情)。

因此,我的问题是:如何启动具有快照隔离级别的事务,并使其在窗口打开的时间内保持打开状态?事务保持打开是绝对必要的,这样我就可以继续从连接中读取数据,而不会同时读取其他用户添加的行。

我知道我可以用原始的SQL命令做到这一点,但如果可能的话,我想避免这种情况。

旁注:人们对不同的隔离级别有不同的看法,但这个问题不是为了讨论快照隔离级别在这种情况下是否合适。SNAPSHOT可以很好地满足我们这项任务的业务需求。这个问题实际上也可能是关于任何其他隔离级别的,因为其他隔离级别也不适用于此代码。

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2010-11-11 19:24:32

抱歉,我一直在浪费你的时间。令我惊讶的是,我发布的代码实际上是有效的。我使用SQL Profiler测试了我的程序,并查找了"BEGIN TRANSACTION“语句和"SET TRANSACTION ISOLATION LEVEL SNAPSHOT”。但事实证明,要跟踪事务,您需要在SQL Profiler的事件列表中专门选择它们。我怎么不知道。我认为Profiler中的事务会像普通的SQL命令一样被跟踪。此外,我发现SQL Profiler无法跟踪事务隔离级别的更改。要找出事务所处的事务隔离级别,必须查询sys.dm_exec_sessions系统视图。它有一个名为"transaction_isolation_level“的列,该列有一个与隔离级别相对应的数值。您可以在documentation for the view中看到这个数字的含义。

当我意识到这一点时,我尝试了我的原始代码,并查询了视图,看看!它确实处于快照隔离级别。

我希望这能为别人节省一些时间。:-)

票数 6
EN

Stack Overflow用户

发布于 2010-11-11 02:47:08

使用TransactionOptions控制系统事务作用域的隔离级别:

代码语言:javascript
复制
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隔离级别。如果在数据库中启用了read_committed_snapshot,则还可以使用ReadCommitted隔离级别。

作为一般规则:

  • 最好在操作期间打开连接,然后立即将其关闭。连接池将从那里接管它。
  • 在窗体的生命周期中绝对禁止持有事务。在特定操作的持续时间内,事务只能在堆栈作用域上执行。只需点击一个按钮)。否则健忘的弗雷德会打开表单去吃午饭,用他的挂起的transaction.

冻结整个数据库

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/4146483

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档