EF,POCO,DB首先......如何在财产“集合”中做业务逻辑?

时间:2012-09-13 00:34:40

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

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

我使用DbContext T4生成生成了我的POCO类。我有一些非常好的表格,我的MVC课程中有很多性感的泛型,以减少锅炉板代码......一切都很好。

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

E.g。假设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的set访问器限定为“internal ”。这里的缺点是MVC将尝试从表单更新模型,并且无法设置这些属性。

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

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

    [5。]另一个想法,因为字段总是简单地计算,所以应该真的没有必要设置它们 - 无论是从数据库还是其他方面。因此,这样的事情可能有用:

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

    ...我的想法是EF实例化会尝试调用“set”,但是set无效,然后,当对象被保存或检查更改时,它会调用'get',那就是返回计算值,该值将存储在DB中。这里唯一的缺点是,当数据库存储在ExtendedAmount字段中的值不正确时,您尝试使用对象模型来验证数据库。我知道,这是一个小小的新手,但我认为这将是一个有趣的技巧......实际上,如果(值!= UnitPrice * Quantity)

    ,“set”可能会抛出异常

    ---结束编辑---

    我很想知道其他人在这类案件中所做的事情,因为我确信这很常见。好像很多教程都会把你带到“从数据库中生成POCO类”,然后将剩下的项目开发留给你。

    干杯, 克里斯

1 个答案:

答案 0 :(得分: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(如果尚未实施)并将您的业务逻辑放在单独的代码文件中(但具有相同的类名) 。这样,生成时您的主代码文件将被覆盖,但您的辅助代码文件将保留。这是处理生成代码的常用方法。