【问题标题】:EF, POCO, DB First... how to do Business Logic in Property "set"?EF、POCO、DB First...如何在“设置”属性中进行业务逻辑?
【发布时间】:2012-09-06 01:06:56
【问题描述】:

好的,所以我一直在构建我的第一个大型(ish)EF 4.1 POCO + MVC 应用程序。它是旧系统的替代品,所以我使用的是现有数据库。

我已经使用 DbContext T4 生成生成了我的 POCO 类。我有一些非常好的表单正在进行,并且在我的 MVC 类中使用许多性感的泛型进行了一些非常好的验证,以减少样板代码......一切都很好。

突然间,我意识到(对我而言)最明智的做法是让某些业务逻辑位于我的 POCO 对象的某些属性的“集合”中。

例如假设下面的类是由 T4 生成的;

public partial class SalesOrderLine
{
    public int ID { get; set; }
    public int SalesOrderID { get; set; }
    public int ProductID { get; set; }
    public decimal UnitPrice { get; set; }
    public int Quantity { get; set; }
    public decimal ExtendedPrice { get; set; }

    public virtual Product Product { get; set; }
    public virtual SalesOrder SalesOrder { get; set; }
}

暂时忽略一个明显的论点,即计算字段“ExtendedPrice”甚至不应该存储在数据库中,和我一起去兜风......

...那么,在我看来,从逻辑上讲,如果这个对象真的应该代表一个销售订单行,那么我应该能够构造我的对象,以便以下单元测试可以工作:

SalesOrderLine sol = new SalesOrderLine();
sol.UnitPrice = 100;
sol.Quantity = 5;
Assert.IsEqual(sol.ExtendedPrice, 500);

...显然,只要我希望 T4 生成基础 POCO,我就不能这样做。在我看来,我有几个选择:

  1. 设置生成代码文件的属性“不编译”,将生成的代码复制粘贴到另一个文件中,修改“set”做设置UnitPrice或Quantity时设置扩展价格的业务逻辑.这里的缺点是,只要从数据库加载对象,就会运行逻辑(因为 EF 将设置公共属性而不是我的私有字段)。此外,当数据库发生更改时,该对象将需要在项目的剩余生命周期内手动维护。

  2. 创建一个 UpdateTotals 函数,该函数在我的对象的 Validate 例程中调用,该函数由 DbContext 上的 SaveChanges() 调用。显然,上面的单元测试在这种情况下不起作用。然而,系统和我的集成测试可以工作,并且只会在对对象进行更改时调用代码。

  3. 确定我问错了问题,我应该真正向名为“SetPrice”和“SetQuantity”的对象添加方法,然后将 UnitPrice 和 Quantity 的集合访问器限定为“内部” ”。这里的缺点是 MVC 将尝试从表单更新模型,并且无法设置这些属性。

  4. 一些解决方案涉及下载两个或三个以上的框架,这些框架创建的抽象级别比我已经拥有的还要多……存储库模式,或“使用 NHibernate”或类似的东西……你可以建议这个,但我越来越厌倦了以“学术上正确”的方式进行设置的工作量。对于这个项目,我宁愿在长期可维护性与开发速度方面进行中途会面,而不是用大量额外的工具和 dll 使我的项目过于复杂......但我会试着保持开放的心态:)

--- 编辑:另一个想法---

[5.] 另一个想法,因为字段总是简单地计算,所以真的不需要设置它们——无论是从数据库还是其他地方。因此,这样的事情可能会起作用:

 public decimal ExtendedAmount
 {
     get { return UnitPrice * Quantity; }
     internal set { }
 }

...我的想法是 EF 实例化会尝试调用“set”,但 set 什么都不做,然后,当对象被保存或检查更改时,它会调用“get”,这将返回计算的值,该值将存储在数据库中。这里唯一的缺点是当您尝试使用对象模型来验证数据库时,当数据库的 ExtendedAmount 字段中存储的值不正确时。我知道这有点古怪,但我认为这将是一个有趣的技巧......事实上,如果 (value != UnitPrice * Quantity),“set”可能会引发异常

--- 结束编辑 ---

我很想知道其他人在这类情况下做了什么,因为我相信这很常见。似乎很多教程都将您带到“从数据库生成 POCO 类”,然后将其余的项目开发留给您。

干杯, 克里斯

【问题讨论】:

    标签: asp.net-mvc-3 entity-framework poco


    【解决方案1】:

    几个想法:

    1. 为什么不使用Code First?这样,您就可以将业务逻辑(例如,计算的属性)直接放在您的实体类中。

      例子

      public partial class SalesOrderLine
      {
          public int ID { get; set; }
          public int SalesOrderID { get; set; }
          public int ProductID { get; set; }
      
          private decimal _unitPrice;
          public decimal UnitPrice
          {
               get { return _unitPrice; }
               set
               {
                    if (value == _unitPrice) return;
                    _unitPrice = value;
                    CalculateExtendedPrice();
               }
          }
      
          private decimal _quantity;
          public decimal Quantity
          {
               get { return _quantity; }
               set
               {
                    if (value == _quantity) return;
                    _quantity= value;
                    CalculateExtendedPrice();
               }
          }
      
          public decimal ExtendedPrice { get; set; }
      
          public virtual Product Product { get; set; }
          public virtual SalesOrder SalesOrder { get; set; }
      
          private void CalculateExtendedPrice()
          {
              ExtendedPrice = UnitPrice * Quantity;
          }
      }
      
    2. 如果 Code First 不是一个选项,那么将您的实体设置为 partial class(如果还没有的话)并将您的业务逻辑放在单独的代码文件中(但具有相同的类名)怎么样。这样,您的主代码文件将在您生成时被覆盖,但您的辅助代码文件将保留。这是处理生成代码的常用方法。

    【讨论】:

    • 1.我从一个遗留数据库开始,所以我从中生成了代码,但现在我想我可能会从这里开始使用代码优先(或进行双重维护)。我想我仍然对框架将加载所有价格字段并因此为从数据库中实例化的每个实体调用我的计算代码这一事实感到有些困扰。
    • 2.部分类不能覆盖属性设置/获取访问器 - 可以吗? (也许我在这里遗漏了一些东西)。请注意,我的部分类中已经有一堆新函数和只读计算属性,但是 AFAIK,您不能在两个部分类文件中定义具有相同名称的相同属性。
    • 关于 2,唉,不,您不能从另一个部分类覆盖一个部分类的属性。但是,如果生成的代码实现了INotifyPropertyChanged,您可以在自定义分部类中处理PropertyChanged 事件并从那里开始计算。至于每次加载实体时都会发生计算的烦恼,它应该只有在它导致性能下降时才是一个问题。如果计算简单,这应该不是问题。
    猜你喜欢
    • 2012-11-08
    • 1970-01-01
    • 2022-11-24
    • 2018-03-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多