利用 Linq to SQL 的数据访问层开发方式讨论

为了较好的开发体验,在开发阶段,我喜欢用 Model -> DB 的次序来进行。
也就是说,先在程序中创建一个 Linq to SQL Data Classes 类,在设计视图上设计好类图。然后,程序中执行一下如下的代码来生成数据库:

if (db.DatabaseExists())
    db.DeleteDatabase();
db.CreateDatabase();

这样做的好处是让我们可以以面向对象的方式去设计程序,而不必过早的纠缠于数据库的细节中去。

如果反过来,DB -> Model:

先在 SQL Server 2005 中设计好表,手工设置外键等细节,然后将这些表拖放到设计界面上去生成类。


这样的步骤,外键设置很容易出错,而且不容易设计继承关系。
像单表继承这样的工作,最终还是要在 dbml 的设计视图中去设置的。

还有一个坏处,就是生成的 Association,默认是双向关联的。但很多有时候我们需要的只是单向的关联,这也要到设计界面中去自己删除。
比如一个人可以有几个通讯地址:
生成 staff.Addresses 是有意义的,
而反过来 address.Staffs 就意义不大。

这样做有一个不好,就是每次调整设计(dbml)后,要重新生成数据库就会冲掉之前的测试数据。
为了这个目的,我们可以学习 Rails 中的思路,创建一个测试固件(Test Fixtures)项目.可以做成 Console Application, 加到解决方案中。
每次调整设计后,执行一遍即可。

在看了 Kigg Starter Kit 代码之后,我发现可以参考它的思路。我们可以定义一个 IDataService 接口,其中定义各种数据操作。
然后,再创建一个 DataContext 的 partial class. 让它继承这个接口,在其中包装一些相应的 linq to sql 操作,这样,UI 层的代码就可以做到很简单。

为了做到不和 Linq to SQL 的具体实现解耦,以便在将来也许需要替换掉 Linq to SQL 的实现。我们在接口层始终使用 IDataService 来操作数据,其具体的 Linq to SQL 实现可以用依赖注入框架来配置,如 Unity Application Block 等。

但这里我仍有疑问:实体如何传递呢?

一种办法是自己再定义一个独立的 Model 层,然后我们把 IDataService 的操作中返回的每一个结果都以 Model 层里定义的实体来表示。虽然利用 .NET 3.5 里的语法写起来不算太麻烦,但因为 Linq to SQL 已经有了实体定义了,自己再创建一层势必造成非常多的重复。扩展性是好了,但维护起来不容易。

另外,这样做也带来很多麻烦。比如在 Linq to SQL 里我们很多关联的实体集合可以设置为惰性加载。而返回自定义的实体集合后,就丧失了这个优势。否则,原来在 aspx 的界面里可以方便地: Eval("OrderDetails[0].UnitPrice") 获取关联信息,现在难道都要一次性加载出来吗?

从这个角度来看,使用 Linq to SQL 后,开发方便了,但是要做到清晰的分层,不太容易。
也许有人会说 ADO.NET Entity Framework 能做到这一点,但因为该框架还未正式发布,我也没有研究,这里不讨论。

所以,目前我的 Linq to SQL 开发方式是,创建 Data Context 的 partial class 来封装操作,以简化 UI 层的操作代码为指导原则。而需要传递实体集的地方,就仍然使用 Linq to SQL 产生的映射类。
如果 UI 层需要用到一个自定义的视图,则定义一个表示该视图的数据类用来返回。

大家有什么好的建议,欢迎讨论。




posted on 2008-05-15 12:20  NeilChen  阅读(3784)  评论(22编辑  收藏  举报

导航