LINQ to SQL和仓储模式

43

我感觉自己在打转。似乎无法确定使用LINQ to SQL的正确存储库模式。如果您熟悉Rob ConeryMVC商店前端,您将看到他的实现将由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版本中建议的使用IRepository模式与LINQ to SQL中直接从存储库返回DTO对象的方式?
业务逻辑应该在哪里执行和检查?这是在由存储库在保存/更新时调用的单独层中完成,还是内置于包装类中?
3个回答

48
LINQ to SQL不是一个真正的对象关系映射器(ORM),而是一个数据访问层生成器。你可以通过手动编辑XML文件和使用SqlMetal等工具来使其成为ORM,但它最擅长的是作为DAL
ORM的理念是这样的。你有你的SQL数据库和领域对象。为了正确设计数据库,你会做一些逻辑上无法转换为正确设计的对象模型的事情(例如规范化),反之亦然。这被称为“阻抗失配”,ORM的作用是以一种清洁、有效和高效的方式处理这种失配。减少痛苦的数据库交互几乎是次要的事情。
仓储的理念是将所有持久性逻辑和基础架构依赖封装在应用程序的其他部分之外。当你的应用程序需要一个客户对象时,它不应该知道它是来自SQL Server、MySQL、XML文件还是ASP.NET Membership。一旦你实现了这种解耦,你对持久性故事所做的任何更改都不会影响你的应用程序的其他部分。
考虑到这一点,他为什么要做这件事情就更加清晰了。LINQ to SQL用于生成数据访问层(DAL),但是关于DAL应该知道的唯一事情就是存储库(repository),因此对其进行了转换以适应其领域对象。这样,他可以重构其领域模型而不必担心其持久性故事,并且可以重构其数据库而不必担心对应用程序产生连锁反应。他还可以在决定使用何种ORM甚至在哪里存储数据之前开始编写业务逻辑。
如果他使用真正的ORM(例如NHibernate),则映射代码将在其他地方处理(无论是在XML中还是在引导类中)。我认为LINQ to SQL(和Robs开源的DAL,SubSonic)是很棒的项目,但更适用于较小的两层应用程序,在那里像存储库模式这样的东西就过度了。商店也很好地说明了NHibernate的额外复杂性为什么很重要。他本可以通过选择一些专门处理这种情况的东西而不是手动完成所有操作来节省大量代码。

但是,如果您将LINQ到SQL扩展方法用于存储库,一旦切换到NHIBERNATE之类的东西,该怎么办?创建一个新的存储库吗? - zsharp
这是一个很好的解释。我认为很多人(包括我)遇到的问题是试图将L2S对象视为不仅仅是数据访问层。但是,难道这不是微软为验证等构建各种钩子的一部分的错吗? - Micah
1
如果只需要2层架构,那么没有任何问题。在这种情况下,L2S是一个很棒的产品,它生成的模型对象非常可定制和可扩展。问题在于向n层模式进行扩展,我觉得像L2S这样的项目开始崩溃。 - Matt Briggs

13
这取决于DTOs的定义位置和您想要如何测试它。如果使用DBML,则LINQ to SQL希望在数据层中生成数据对象。虽然LINQ to SQL支持持久性无知,但它并没有尽最大努力使其易于实现。Entity Framework根本不支持。
这意味着在标准模型中,数据层定义了所有领域实体,如果要真正将用户界面/业务层与数据层隔离开来进行测试,则会变得棘手。
一种实用的方法可能是在单元测试中使用数据层中的数据对象定义,但不使用数据上下文(即将数据上下文隐藏在存储库接口后面,但公开实体类型)-但这会使事情变得有些混乱,并意味着您的UI等需要强烈引用数据层。但如果将其视为“可能正在使用的存储库实现的领域模型层”,则可以对其进行合理解释。
保持完全分离的领域实体使单元测试和控制反转(IoC)更加“纯粹”,但增加了代码量(因此具有双重优势)。

2

生成的对象是否可序列化?我有印象它们不能。正如Marc Gravel所说,这只是一种隔离情况。

如果你更换存储库并使用MySQL、Oracle、XML文件、Web服务或其他数据提供者(存储库),你将被绑定到LINQ to SQL程序集来引用实体,对吗?当然,你不希望这样。


您可以将它们作为选项进行序列化。 - Micah

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接