【问题标题】:How can I reject all changes in a Linq to SQL's DataContext?如何拒绝 Linq to SQL DataContext 中的所有更改?
【发布时间】:2008-11-03 16:41:14
【问题描述】:

在 Linq to SQL 的 DataContext 上,我可以调用 SubmitChanges() 来提交所有更改。

我想要的是以某种方式拒绝数据上下文中的所有更改并回滚所有更改(最好不进入数据库)。

这可能吗?

【问题讨论】:

    标签: linq linq-to-sql


    【解决方案1】:

    为什么不丢弃数据上下文并简单地将其替换为新实例?

    【讨论】:

    • 因为我可能有通过上下文获取的对象,我可能想在以后更改/使用它。如果我丢弃数据上下文,我必须再次获取这些对象。这是一个 Windows 服务,数据上下文在其中存在很长时间。
    • DataContext 旨在用于工作单元方法,因此不打算长期存在。见:Lifetime of a LINQ to SQL DataContext
    【解决方案2】:
    public static class DataContextExtensions
    {
        /// <summary>
        ///     Discard all pending changes of current DataContext.
        ///     All un-submitted changes, including insert/delete/modify will lost.
        /// </summary>
        /// <param name="context"></param>
        public static void DiscardPendingChanges(this DataContext context)
        {
            context.RefreshPendingChanges(RefreshMode.OverwriteCurrentValues);
            ChangeSet changeSet = context.GetChangeSet();
            if (changeSet != null)
            {
                //Undo inserts
                foreach (object objToInsert in changeSet.Inserts)
                {
                    context.GetTable(objToInsert.GetType()).DeleteOnSubmit(objToInsert);
                }
                //Undo deletes
                foreach (object objToDelete in changeSet.Deletes)
                {
                    context.GetTable(objToDelete.GetType()).InsertOnSubmit(objToDelete);
                }
            }
        }
    
        /// <summary>
        ///     Refreshes all pending Delete/Update entity objects of current DataContext according to the specified mode.
        ///     Nothing will do on Pending Insert entity objects.
        /// </summary>
        /// <param name="context"></param>
        /// <param name="refreshMode">A value that specifies how optimistic concurrency conflicts are handled.</param>
        public static void RefreshPendingChanges(this DataContext context, RefreshMode refreshMode)
        {
            ChangeSet changeSet = context.GetChangeSet();
            if (changeSet != null)
            {
                context.Refresh(refreshMode, changeSet.Deletes);
                context.Refresh(refreshMode, changeSet.Updates);
            }
        }
    }
    

    参考Linq to SQL - Discard Pending Changes

    【讨论】:

    • 哇,我喜欢这个,这在我的 C# DotNet 4.0 代码中工作得很好。泰迪你统治!我不明白为什么微软一开始就没有将它内置到数据上下文中。我在 DiscardPendingChanges() 之后立即执行了 db.SubmitChanges() 以验证是否没有发生任何更改。简直太美了。
    • @Omzig 这在长期环境中很有用。在短期上下文中,另一种方法是简单地重新初始化数据上下文。所以我认为这就是微软没有在实体框架中包含这种方法的原因,他们认为在大多数情况下它是一个短生命周期的上下文。
    【解决方案3】:

    在 .net 3.0 中,db.GetChangeSet().Updates.Clear() 用于更新,db.GetChangeSet().Inserts.Clear() 用于新项目,db.GetChangeSet().Deletes.Clear() 用于已删除项目。

    在 .net 3.5 及更高版本中,GetChangeSet() 的结果现在是只读的,在 for 或 foreach 中循环集合并刷新每个 ChangeSet 表,就像 macias 在他的评论中所写的那样。

    【讨论】:

    • 这是公认的答案?这不起作用——Updates/Inserts/Deletes 集合是只读的。并且 MSDN 文档说这些集合是“在调用时计算的”(GetChangeSet())。 msdn.microsoft.com/en-us/library/…
    • 对我不起作用(VS2008)——我得到异常“集合是只读的”。但是将它放入 foreach 循环并刷新 ChangeSet 中的每个表 - 有效。
    • 是的,这是只读的,这不起作用,因为它来自 GetChangeSet() get = readonly
    • 请先阅读!如果您不喜欢这个答案,请选择另一个答案或写下您的评论,但请不要拒绝这个答案。我无法删除它,因为它已被接受。
    【解决方案4】:

    正如 Haacked 所说,只需删除数据上下文。

    您可能不应该长时间保持数据上下文处于活动状态。它们旨在以事务方式使用(即每个原子工作单元一个数据上下文)。如果您长时间保持数据上下文处于活动状态,则在更新陈旧实体时产生并发异常的风险更大。

    【讨论】:

      【解决方案5】:

      对 Updates、Delete 和 Inserts 集合调用 Clear() 不起作用。

      GetOriginalEntityState() 可能很有用,但它只提供外键关系的 ID,而不是实际实体,因此您会得到一个分离的对象。

      这里有一篇文章解释了如何从数据上下文中丢弃更改:http://graemehill.ca/discard-changes-in-linq-to-sql-datacontext

      编辑:调用 Refresh() 将撤消更新,但不会删除和插入。

      【讨论】:

        【解决方案6】:

        刷新将起作用,但是您必须提供要重置的实体。

        例如

        dataContext.Refresh(RefreshMode.OverwriteCurrentValues, someObject);
        

        【讨论】:

        • 这将访问数据库以获取最新的值,它不只是恢复到旧的缓存值。
        【解决方案7】:

        您可以使用 GetOriginalEntityState(..) 来获取对象的原始值,例如使用旧缓存值的客户。

        您还可以遍历更改,例如仅更新和刷新特定对象而不是整个表,因为性能损失会很高。

        foreach (Customer c in MyDBContext.GetChangeSet().Updates)
                {
                    MyDBContext.Refresh(System.Data.Linq.RefreshMode.OverwriteCurrentValues, c);
                }
        

        这将使用数据库中的持久数据恢复更改。

        另一种解决方案是使用 Dispose() 转储您使用的数据上下文。

        在任何情况下,覆盖例如集合中的 Insert 和 Remove 方法都是一个好习惯。您使用和添加的客户,例如一个 InsertOnSubmit() 调用。这将解决您的待处理插入和删除问题。

        【讨论】:

          【解决方案8】:

          我的应用程序是带有图标的 Outlook 样式,用于选择活动表单 (ListBox)。在允许用户更改其上下文之前,他们必须接受更改或丢弃它们。

          var changes = db.GetChangeSet();
          if ((changes.Updates.Count > 0) || (changes.Inserts.Count > 0) || (changes.Deletes.Count > 0))
          {
              if (MessageBox.Show("Would you like to save changes?", "Save Changes", MessageBoxButton.YesNo)  == MessageBoxResult.Yes)
              {
                  db.SubmitChanges();
              } else
              {
                  //Rollback Changes
                  foreach (object objToInsert in changes.Inserts)
                  {
                      db.GetTable(objToInsert.GetType()).DeleteOnSubmit(objToInsert);
                  }
                  foreach (object objToDelete in changes.Deletes)
                  {
                      db.GetTable(objToDelete.GetType()).InsertOnSubmit(objToDelete);
                  }
                  foreach (object objToUpdate in changes.Updates)
                  {
                      db.Refresh(RefreshMode.OverwriteCurrentValues, objToUpdate);
                  }
                  CurrentForm.SetObject(null); //Application Code to Clear active form
                  RefreshList(); //Application Code to Refresh active list
              }
          }
          

          【讨论】:

            【解决方案9】:

            here 上写得很好,但这里是所用代码的复制和粘贴。

            Public Sub DiscardInsertsAndDeletes(ByVal data As DataContext)
                ' Get the changes
                Dim changes = data.GetChangeSet()
            
                ' Delete the insertions
                For Each insertion In changes.Inserts
                    data.GetTable(insertion.GetType).DeleteOnSubmit(insertion)
                Next
            
                ' Insert the deletions
                For Each deletion In changes.Deletes
                    data.GetTable(deletion.GetType).InsertOnSubmit(deletion)
                Next
            End Sub
            
            Public Sub DiscardUpdates(ByVal data As DataContext)
                ' Get the changes
                Dim changes = data.GetChangeSet()
            
                ' Refresh the tables with updates
                Dim updatedTables As New List(Of ITable)
                For Each update In changes.Updates
                    Dim tbl = data.GetTable(update.GetType)
                    ' Make sure not to refresh the same table twice
                    If updatedTables.Contains(tbl) Then
                        Continue For
                    Else
                        updatedTables.Add(tbl)
                        data.Refresh(RefreshMode.OverwriteCurrentValues, tbl)
                    End If
                Next
            End Sub
            

            【讨论】:

            • 谢谢你;该网站似乎已不存在。
            • 如果有人仍然使用 LINQ to SQL,那么仅供参考,我修复了链接 :)
            • 遗憾的是,这种方法在连接大量实体时非常缓慢。但仍然是一个聪明的方法。
            【解决方案10】:

            这就是我的做法。我只是按照上面 Teddy 的例子进行了简化。不过,我有一个问题,为什么还要费心在 DELETES 上刷新?

              public static bool UndoPendingChanges(this NtsSuiteDataContext dbContext)
              {
                 if (dbContext.ChangesPending())
                 {
                    ChangeSet dbChangeSet = dbContext.GetChangeSet();
            
                    dbContext.Refresh(RefreshMode.OverwriteCurrentValues, dbChangeSet.Deletes);
                    dbContext.Refresh(RefreshMode.OverwriteCurrentValues, dbChangeSet.Updates);
            
                    //Undo Inserts
                    foreach (object objToInsert in dbChangeSet.Inserts)
                    {
                       dbContext.GetTable(objToInsert.GetType()).DeleteOnSubmit(objToInsert);
                    }
            
                    //Undo deletes
                    foreach (object objToDelete in dbChangeSet.Deletes)
                    {
                       dbContext.GetTable(objToDelete.GetType()).InsertOnSubmit(objToDelete);
                    }
                 }
            
                 return true;
              }
            

            【讨论】:

              猜你喜欢
              • 2012-05-20
              • 1970-01-01
              • 1970-01-01
              • 2011-01-03
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2012-12-25
              相关资源
              最近更新 更多