1

好的,所以我一直在构建我的第一个大型(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 的 set 访问器限定为“内部”。这里的缺点是 MVC 将尝试从表单更新模型,并且无法设置这些属性。

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

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

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

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

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

--- 结束编辑 ---

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

干杯,克里斯

4

1 回答 1

1

几个想法:

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

    例子

    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 不是一个选项,那么如何让您的实体成为部分类(如果还没有的话)并将您的业务逻辑放在单独的代码文件中(但具有相同的类名)。这样,您的主代码文件将在您生成时被覆盖,但您的辅助代码文件将保留。这是处理生成代码的常用方法。

于 2012-09-13T01:00:32.983 回答