【问题标题】:Saving Database Entities保存数据库实体
【发布时间】:2015-07-19 07:51:19
【问题描述】:

我有一个 C# 程序,可以将数据库中的产品列表加载到产品对象列表中。用户可以通过我的程序界面添加新产品、编辑产品和删除产品。很标准的东西。我的问题与跟踪这些更改并将它们保存回数据库有关。在详细介绍之前,我知道使用 Entity Framework 或 NIBerate 之类的东西可以解决我关于跟踪添加和编辑的问题,但我认为它不会解决我关于跟踪删除的问题。除了想要将大型代码库转换为使用 Entity Framework 或 NIBerate 的替代方法之外,出于我自己的好奇心,我还想知道这个问题的答案。

为了跟踪编辑,我在 Product 类上做这样的事情,我在任何时候更改属性时设置 IsDirty 标志:

class Product
{
   public bool IsDirty { get; set; }
   public bool IsNew { get; set; }

   // If the description is changed, set the IsDirty property
   public string Description
   { 
      get
      {
         return _description;
      }
      set
      {
         if (value != _description)
         {
            this.IsDirty = true;
            _description = value;
         }         
      }
   }
   private string _description;

   // ... and so on
}

当我创建一个新的 Product 对象时,我设置了它的 IsNew 标志,因此程序知道在用户下次保存时将其写入数据库。成功将产品写入数据库后,我会清除其 IsNew 和 IsDirty 标志。

为了跟踪删除,我创建了一个 List 类来跟踪已删除的项目:

class EntityList<T> : List<T>
{
   public List<T> DeletedItems { get; private set; }

   EntityList()
   {
      this.DeletedItems = new List<T>();
   }

   // When an item is removed, track it in the DeletedItems list
   public new bool Remove(T item)
   {
      this.DeletedItems.Add(item);
      base.Remove(item);
   }
}

// ...

// When I work with a list of products, I use an EntityList instead of a List
EntityList<Product> products = myRepository.SelectProducts();

每次我将产品列表保存到数据库时,我都会遍历 EntityList.DeletedItems 属性中的所有产品并从数据库中删除这些产品。成功保存列表后,我会清除 DeletedItems 列表。

所有这些工作,但似乎我可能做的工作太多,特别是跟踪已删除的项目,并记住每次创建新产品对象时设置 IsNew 标志。我无法在 Product 的构造函数中设置 IsNew 标志,因为如果我从数据库加载 Product 对象,我不希望设置该标志。我也对我必须在任何地方使用我的 EntityList 类而不是使用 List 的事实感到不高兴。

这种情况似乎非常普遍,但我无法通过我的研究找到一种优雅的方式来做到这一点。所以我有两个问题:

1) 假设我没有使用实体框架之类的东西,有没有更好的方法来跟踪添加、编辑和删除,然后将这些更改保存到数据库中?

2) 我是否正确地说,即使使用 Entity Framework 或 NIBerate,我仍然需要编写一些额外的代码来跟踪我已删除的项目?

【问题讨论】:

    标签: c# database entity-framework


    【解决方案1】:

    在 EF 中,DbContext 对象包含跟踪它所知道的对象更改的所有逻辑。当您可以SaveChanges 时,它会确定发生了哪些更改并执行适当的操作以将这些更改提交到数据库。您无需对对象状态执行任何特定操作,只需在您想要添加或删除记录时通知DbContext。

    更新:

    当您查询DbSet 时,EF 会在内部跟踪您获得的对象。在SaveChanges 期间,将这些对象的当前状态与它们的原始状态进行比较,并将更改的那些放入队列中以在数据中更新。

    插入:

    当您将新对象添加到相关的DbSet 时,它会在SaveChanges 调用期间被标记为插入。对象已注册到更改跟踪中,它的 DB 生成字段(例如自动增量 ID)已更新,等等。

    删除:

    要从您在相关DbSet 上调用Remove 的数据库中删除一条记录,EF 将在下一次SaveChanges 调用期间执行该操作。

    因此,您无需担心为了数据库而跟踪这些更改,一切都会为您处理。您可能需要了解自己的利益 - 例如,有时能够为更改的记录着色是件好事。

    上述情况也适用于 Linq2SQL 以及可能的其他 ORM 和数据库接口层,因为它们的主要目的是让您无需编写大量代码即可访问数据。

    【讨论】:

      【解决方案2】:
      1. 有没有更好的方法来跟踪添加、编辑和删除,然后将这些更改保存到数据库中?

      Entity Framework 和 NHibernate 都选择不让实体自己负责通知或跟踪其更改*。所以这不是一个糟糕的选择。从设计模式的角度来看,这无疑是一个不错的选择(单一职责)。

      它们分别在上下文或会话中存储从数据库加载数据时的数据快照。此外,这些快照的状态表明它们是新的、更新的、删除的还是未更改的。并且有一些过程可以比较实际值和快照并更新实体状态。当需要保存更改时,会评估状态并生成适当的 CRUD 语句。

      这一切都非常复杂,要自己实现。我什至没有提到实体状态及其相互关联的完整性。但是,一旦您决定遵循相同的模式,它当然是可行的。数据层通知/跟踪更改(而不是实体本身)的优点是 DAL 知道哪些更改与数据存储相关。并非所有属性都映射到数据库表,但实体不知道这一点。

      1. 我仍然需要编写一些额外的代码来跟踪我删除的项目吗?

      没有。两个 OR 映射器都有 persistence ignorance 的概念。您基本上只使用内存中的对象,这可能包括从列表中删除它们(嵌套在所有者实体或表示数据库表的列表中),并且 ORM 知道如何将实体的内存状态与数据库同步.


      *Entity Framework 曾经有自跟踪实体,但已被弃用。

      【讨论】:

      • 由于 Entity Framework 正在存储我的数据的快照,这会产生多少开销?它本质上是否将每个对象存储两次(当前工作副本和原始副本),从而使使用的内存量翻倍?
      • 它将副本存储为值列表,工作副本是物化的对象,您可以使用它。这是开销吗?它是整个过程中不可或缺的一部分。无论如何,始终建议使用短暂的上下文(或会话),否则它们会变得非常沉重和缓慢。
      猜你喜欢
      • 2014-07-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-11-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多