代码之家  ›  专栏  ›  技术社区  ›  René

在实体框架4中读取数据时,如何使用事务?

  •  0
  • René  · 技术社区  · 15 年前

    要使用快照隔离级别,必须在数据库中启用它。我做到了。我使用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命令来完成,但如果可能的话,我想避免这样做。

    旁注:人们在不同的隔离级别上有不同的操作,但这个问题不是为了讨论在这种情况下快照隔离级别是否合适。快照与此任务的业务需求完美配合。问题也可能是关于任何其他隔离级别,因为其他隔离级别也不适用于此代码。

    2 回复  |  直到 15 年前
        1
  •  6
  •   René    15 年前

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

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

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

        2
  •  2
  •   Remus Rusanu    15 年前

    使用 TransactionOptions

    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 隔离级别。您还可以使用 ReadCommitted

    作为一般规则:

    • 最好在操作期间打开连接并立即关闭。连接池将从那里接管它。
    • 绝对禁止在表单的生命周期内保存事务。事务可以生存 只有