LINQ to SQL и шаблон репозитория - PullRequest
44 голосов
/ 20 января 2009

Мне кажется, что я бегаю кругами. Я не могу определиться с тем, какой правильный шаблон репозитория использует 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)
                };
        }

В чем преимущество такого способа (с использованием класса-обертки) по сравнению с тем, что Майк Хэдлоу предлагает в Использование шаблона IRepository с LINQ to SQL в своем версия IRepository , где он просто возвращает объекты DTO из репозитория?

Где следует применять и проверять бизнес-логику? Это все в отдельном слое, которое вызывается хранилищем при сохранении / обновлении, или это встроено в класс-оболочку?

Ответы [ 3 ]

48 голосов
/ 21 января 2009

Дело в том, что LINQ to SQL не является истинным Object Relation Mapper (ORM), это генератор уровня доступа к данным. Вы можете сделать это ORM, углубившись вручную, редактируя файлы XML и играя с SqlMetal и еще чем угодно, но где он сияет, как DAL .

Идея ORM заключается в следующем. У вас есть база данных SQL и ваши доменные объекты. Чтобы правильно спроектировать базу данных, вы собираетесь делать вещи (например, нормализацию), которые логически не переводятся в правильно спроектированную объектную модель, и наоборот. Это называется «Несоответствие импеданса», роль ORM состоит в том, чтобы справиться с этим несоответствием чистым, эффективным и действенным способом. Менее болезненное взаимодействие с базой данных - это почти второстепенная вещь.

Идея репозитория заключается в том, что он инкапсулирует всю логику постоянства и зависимости от инфраструктуры от остальной части вашего приложения. Когда вашему приложению требуется объект Customer, ему не нужно знать, исходит ли он от SQL Server, MySQL, файла XML или членства ASP.NET. Как только вы отключите эту связь, любые изменения, внесенные вами в историю постоянства, не окажут влияния на остальную часть вашего приложения.

Имея это в виду, становится более понятно, почему он сделал то, что сделал. LINQ to SQL используется для генерации DAL, но единственное, что следует знать о DAL, - это репозиторий, поэтому выполняется перевод в его доменные объекты. Таким образом он может реорганизовать свою модель предметной области, не беспокоясь о своей истории постоянства, и он может реорганизовать свою базу данных, не беспокоясь о волновых эффектах в своем приложении. Он мог бы также начать писать код бизнес-логики, прежде чем принимать решение, например, о том, какой ORM использовать или даже где хранить свои данные.

Если бы он использовал реальный ORM (например, NHibernate ), этот код отображения обрабатывается в другом месте (либо в XML, либо в классах начальной загрузки). Я думаю, что LINQ to SQL (и DAL Robs с открытым исходным кодом, SubSonic ) - отличные проекты, но они больше предназначены для небольших двухуровневых приложений, в которых что-то вроде шаблона хранилища является излишним. Витрина магазина также является хорошей иллюстрацией того, почему дополнительная сложность NHibernate может быть важной. Он мог бы сэкономить много кода, выполнив что-то, созданное для обработки такого сценария, вместо того, чтобы делать все вручную.

13 голосов
/ 20 января 2009

Это зависит от того, где определены DTO и как вы хотите это проверить. Если вы используете DBML, то LINQ to SQL хочет генерировать объекты данных на уровне данных. Хотя LINQ to SQL поддерживает невежество , оно не делает все возможное, чтобы упростить его. Entity Framework его вообще не поддерживает.

Это означает, что в стандартной модели ваш уровень данных определяет все сущности домена, что сложно, если вы хотите протестировать пользовательский интерфейс / бизнес-уровни в истинной изоляции от уровня данных.

Прагматичный подход может заключаться в использовании определений объектов данных из уровня данных в модульных тестах, но не в контексте данных (то есть скрывать контекст данных за интерфейсом хранилища, но предоставлять типы объектов) - но это немного мутит воду, а это означает, что ваш пользовательский интерфейс и т. д. должны строго ссылаться на уровень данных. Но если вы думаете об этом как о «уровне модели домена, который также содержит реализацию репозитория, которую мы можем или не можем использовать», вы можете оправдать это.

Сохранение полностью отдельных доменных сущностей делает модульное тестирование и инверсию управления (IoC) более "чистым", но увеличивает объем кода, который у вас есть (так что обоюдоострый).

2 голосов
/ 21 января 2009

Сериализуются ли сгенерированные объекты? У меня было впечатление, что это не так. Это просто случай изоляции, как сказал Марк Гравель выше.

Что если вы переключаете хранилище и у вас есть MySQL, Oracle, файлы XML, веб-служба или какой-либо другой поставщик данных (хранилище)? Вы будете привязаны к сборке LINQ to SQL для ссылки на сущности, верно? Что, конечно, вы не хотели бы.

...