好的,所以我一直在构建我的第一个大型(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,我就不能这样做。在我看来,我有几个选择:
将生成的代码文件的属性设置为“不编译”,将生成的代码复制粘贴到另一个文件中,修改“set”,做设置UnitPrice或Quantity时设置扩展价格的业务逻辑。这里的缺点是,只要从数据库加载对象,就会运行逻辑(因为 EF 将设置公共属性而不是我的私有字段)。此外,当数据库发生更改时,该对象将需要在项目的剩余生命周期内手动维护。
创建一个 UpdateTotals 函数,该函数在我的对象的 Validate 例程中调用,该函数由 DbContext 上的 SaveChanges() 调用。显然,上面的单元测试在这种情况下不起作用。然而,系统和我的集成测试将起作用,并且只会在对对象进行更改时调用代码。
确定我问错了问题,我应该真正向名为“SetPrice”和“SetQuantity”的对象添加方法,然后将 UnitPrice 和 Quantity 的 set 访问器限定为“内部”。这里的缺点是 MVC 将尝试从表单更新模型,并且无法设置这些属性。
一些解决方案涉及下载两个或三个以上的框架,这些框架创建了比我已经拥有的更多级别的抽象......存储库模式,或“使用 NHibernate”或类似的东西......你可以提出这个建议,但我正在成长厌倦了以“学术上正确”的方式进行设置的工作量。对于这个项目,我宁愿在长期可维护性与开发速度方面进行中途会面,而不是用大量额外的工具和 dll 使我的项目过于复杂......但我会试着保持开放的心态:)
--- 编辑:另一个想法 ---
[5.] 另一个想法,因为字段总是简单地计算,所以真的不需要设置它们——无论是从数据库还是其他地方。因此,这样的事情可能会起作用:
public decimal ExtendedAmount
{
get { return UnitPrice * Quantity; }
internal set { }
}
...我的想法是 EF 实例化会尝试调用“set”,但该 set 不会做任何事情,然后,当对象被保存或检查更改时,它会调用“get”并返回计算的值,该值将存储在数据库中。这里唯一的缺点是当您尝试使用对象模型来验证数据库时,当数据库的 ExtendedAmount 字段中存储的值不正确时。我知道,这有点古怪,但我认为这将是一个有趣的技巧......事实上,如果 (value != UnitPrice * Quantity),“set”可能会引发异常
--- 结束编辑 ---
我很想知道其他人在这种情况下做了什么,因为我相信这很常见。似乎很多教程都将您带到“从数据库生成 POCO 类”,然后将其余的项目开发留给您。
干杯,克里斯