44

我觉得我在绕圈子跑。对于使用LINQ to SQL的正确存储库模式,我似乎无法下定决心。如果您熟悉Rob Conery 的 MVC Storefront,您会看到他的实现用另一个类包装了 LINQ 生成的模型,并将 LINQ 生成的模型简单地视为数据传输对象(DTO)。它看起来像这样:

//Custom wrapper class.
namespace Data
{
    public class Customer
    {
         public int Id {get;set;}
         public string Name {get;set;}
         public IList<Address> Addresses {get;set;}
    }
}

//Linq-Generated Class - severly abbreviated
namespace SqlRepository
{
    public class Customer
    {
         public int Id {get;set;}
         public string Name {get;set;}
         public EntitySet<Address> {get;set;}
    }
}

//Customer Repository
namespace SqlRepository
{
    public class UserRepository : IUserRepository
    {
        private _db = new DB(); //This is the Linq-To-Sql datacontext

        public IQueryable GetCusomters()
        {
            return
                from c in _db.Customers
                select new Customer // This is the wrapper class not the gen'd one
                {
                   Id = c.Id,
                   Name = c.Name,
                   Addresses = new LazyList(c.Addresses)
                };
        }

这样做的好处是什么(使用包装类),而不是 Mike Hadlow在他的 IRepository<T> 版本中使用带有 LINQ to SQL 的 IRepository 模式建议的方式,他只是从存储库?

应该在哪里执行和检查业务逻辑?这是在保存/更新时由存储库调用的单独层中,还是内置到包装器类中?

4

3 回答 3

49

事情是这样的,LINQ to SQL 不是一个真正的对象关系映射器(ORM),它是一个数据访问层生成器。您可以通过手动编辑 XML 文件并使用SqlMetal和诸如此类的东西进行深入研究,使其成为 ORM ,但它的亮点在于DAL

ORM 背后的想法是这样的。您拥有 SQL 数据库和域对象。要正确设计数据库,您将执行逻辑上无法转换为正确设计的对象模型的事情(如规范化),反之亦然。这被称为“阻抗失配”,ORM 的作用是以干净、有效和高效的方式处理这种失配。不那么痛苦的数据库交互几乎是次要的事情。

存储库背后的想法是,它封装了应用程序其余部分对基础设施的所有持久性逻辑和依赖项。当您的应用程序需要 Customer 对象时,它不必知道它是来自 SQL Server、MySQL、XML 文件还是 ASP.NET Membership。一旦完成了这种解耦,您对持久性故事所做的任何更改都不会影响应用程序的其余部分。

考虑到这一点,他为什么要做他所做的事情就变得更加清楚了。LINQ to SQL 用于生成 DAL,但关于 DAL 唯一应该知道的是存储库,因此对他的域对象进行了翻译。这样他就可以重构他的领域模型而不用担心他的持久性故事,他可以重构他的数据库而不用担心他的应用程序的涟漪效应。他还可以在决定使用什么 ORM 甚至在哪里存储他的数据等问题之前开始编写业务逻辑编码。

如果他要使用真正的 ORM(如NHibernate),则映射代码将在其他地方处理(在 XML 或引导类中)。我认为 LINQ to SQL(和 Rob 开源 DAL、SubSonic)是很棒的项目,但更多的是为较小的两层应用程序设计的,其中存储库模式之类的东西是多余的。店面也很好地说明了 NHibernate 的额外复杂性为何如此重要。他本可以通过使用专为处理这种情况而构建的东西来为自己节省大量代码,而不是手动完成。

于 2009-01-20T22:35:47.447 回答
13

这取决于DTO的定义位置以及您希望如何测试它。如果使用 DBML,则 LINQ to SQL 想要在数据层生成数据对象。虽然 LINQ to SQL 支持持久性无知,但它并没有竭尽全力让它变得简单。实体框架根本不支持它。

这意味着在标准模型中,您的数据层定义了所有域实体,如果您想在与数据层真正隔离的情况下测试用户界面/业务层,这将是很棘手的。

一种实用的方法可能是在单元测试中使用来自数据层的数据对象定义,而不是数据上下文(即,将数据上下文隐藏在存储库接口后面,但公开实体类型)——但这会使水变得混乱一点点,意味着你的 UI 等需要强引用数据层。但是,如果您将其视为“恰好也包含我们可能使用或可能不使用的存储库实现”的域模型层,您可能会证明它是合理的。

保持完全独立的域实体使单元测试和控制反转(IoC) 更加“纯粹”,但会增加您拥有的代码量(因此是双刃剑)。

于 2009-01-20T11:27:08.780 回答
2

生成的对象是否可序列化?我的印象不是他们。正如上面 Marc Gravel 所说,这只是一个孤立的例子。

如果您切换存储库并拥有 MySQL、Oracle、XML 文件、Web 服务或任何数据提供程序(存储库)会怎样?您将被绑定到 LINQ to SQL 程序集以引用实体,对吗?这当然是你不想要的。

于 2009-01-20T22:22:14.870 回答