По моему опыту создания веб-приложений я всегда использовал подход n-уровня. DAL, который получает данные из db и заполняет объекты, и BLL, который получает объекты из DAL и выполняет любую требуемую бизнес-логику, и веб-сайт, который получает его данные отображения из BLL. Недавно я начал изучать LINQ, и большинство примеров показывают запросы, происходящие прямо из кода кода Web-приложения (возможно, я видел только упрощенные примеры). В n-уровневых архитектурах это всегда считалось большим нет-нет.
Я немного не уверен, как создать новое веб-приложение. Я использовал конструктор Server Explorer и dbml в VS2008 для создания связей dbml и объектов. Мне кажется немного непонятным, если dbml будет считаться слоем DAL, если веб-сайт должен вызывать методы в BLL, который затем будет выполнять запросы LINQ и т. Д.
Каковы некоторые общие рекомендации по архитектуре или подходы к создание решения для веб-приложений с использованием LINQ to SQL?Рекомендации по использованию веб-приложений LINQ to SQL
ответ
Боюсь, вы действительно видели слишком упрощенные примеры. LINQ to SQL (System.Data.Linq) - это ваш уровень DAL. Классы L2S генерируют ваш домен (но не путать с Domain-Driven Design). Кроме того, вы все равно можете написать свой бизнес-уровень.
Я всегда стараюсь предотвратить утечку LINQ в SQL DataContext
в слой презентации (ваше веб-приложение). Поэтому он не должен создавать или фиксировать DataContext
. Также вы не должны возвращать IQueryable<T>
объектов на уровень презентации. IMO бизнес-уровень должен иметь полный контроль над временем жизни DataContext
(единица работы) и формой SQL-запросов.
Однако есть несколько вариантов. Некоторые люди стремятся ослабить эти ограничения. Другие даже идут намного дальше. Это зависит от вашего вкуса и размера приложения. Чем больше приложение, тем больше оправдано добавление слоев абстракции.
При отказе IQueryable
и других данных, связанных с оставлением бизнес-уровня, у вас возникнут некоторые интересные проблемы. Например, уровень представления должен проинструктировать бизнес-уровень, как сортировать результаты. Хотя вы можете позволить слою представления сортировать результаты сами, это означало бы, что вам нужно будет получить все данные из базы данных и страницы на уровне презентации, что приведет к очень плохо исполняющейся системе. Существует несколько решений этой проблемы. Во всех случаях вам нужно будет сообщить бизнес-уровню, как сортировать результаты для вас. Решения можно найти здесь, когда вы ищете LINQ dynamic sort. Я сам написал такое решение, here.
Еще одна проблема, которая запрещает IQueryable
с момента выхода из вашего BL, заключается в том, что также объекты домена часто не могут оставить ваш BL. Большинство объектов домена LINQ to SQL будут содержать ленивые загруженные свойства (например, коллекции для других объектов домена). Однако, когда DataContext
контролирует бизнес-уровень, он будет удален до того, как вы вернете результаты на уровень представления. Когда презентация, чем доступ к ленивому загруженному свойству, возникает исключение, поскольку DataContext
уже был удален. Когда вы размещаете DataContext
в своем бизнес-слое, это поведение, конечно, «по дизайну».Разрешение слоя представления на получение ленивых загруженных свойств означает, что BL теряет контроль над запросами, которые отправляются в базу данных, тем самым теряя контроль над производительностью.
Для решения этой проблемы вы должны вернуть объекты передачи данных (DTO) из BL в уровень презентации. DTO будет содержать только данные и нет внутренний DataContext
, и никаких ленивых загруженных свойств. DTO может быть специально отформатирован для фактического запроса. Конечно, DTO приводят к чрезмерной нагрузке на кодирование, поэтому размер вашей системы и потребности в производительности должны ее оправдать. Чтобы облегчить для себя, я склонен ставить методы статической проекции на DTO. Хотя это не соответствует принципу separation of concerns, я считаю это очень практичным решением. Посмотрите, например, на этом CustomerDTO:
public class CustomerDTO
{
public int CustomerId { get; set; }
public string Name { get; set; }
// City is flatterned from Address.City.
public string City { get; set; }
internal static IQueryable<CustomerDTO> AsDTO(IQueryable<Customer> customers)
{
return
from customer in customers
select new CustomerDTO()
{
CustomerId = customer.Id,
Name = customer.Name,
City = customer.Address.City
};
}
}
Это DTO определяет внутренний AsDTO
метод, который способен преобразовать коллекцию Customer
объектов предметной области в коллекцию CustomerDTO
DTOS. Это значительно упрощает преобразование объектов домена в DTO. Посмотрите, например, в этом методе BL:
public static CustomerDTO[] GetCustomersByCountry(string country)
{
using (var db = ContextFactory.CreateContext())
{
IQueryable<Customer> customers =
(from customer in db.Customers
where customer.Address.Country == country
orderby customer.Name, customer.Id);
return CustomerDTO.AsDTO(customers).ToArray();
}
}
Хорошая вещь об этом подходе является то, что, когда вы смотрите на запрос SQL, вы увидите, что только идентификатор клиента, имя и город таблицы адресов будет извлекаться из базы данных. Это связано с тем, что метод AsDTO
переводит один IQueryable
в другой, позволяя LINQ to SQL выполнять полную операцию в базе данных.
Надеюсь, это дает некоторые идеи о том, что вы можете сделать. Конечно, это мой взгляд на предмет и то, что я нашел практичным в моих ситуациях.
LINQ to SQL - это доступ к БД в реализации DAL, если вы хотите разделить между DAL и BLL. Если у вас менее сложное веб-приложение (а также никогда не предназначено для переключения бэкэндов БД), вы можете уйти без явного DAL/BLL и сделать все в коде. LINQ to SQL отлично работает для операций с readonly, но чувствует себя немного больше, чтобы реализовать операции записи.
спасибо за тщательный ответ. Я сжимаюсь при выполнении доступа к данным на уровне презентации. Я обычно делаю большую часть моей сортировки на datalayer в настоящее время, так что это не проблема. Раньше я не слышал о DTO, звучит как-то, что нужно посмотреть дальше. – derek
DTO часто считают, что у них много накладных расходов. Они особенно полезны при отправке данных через провод (например, при использовании веб-служб WCF или ASMX). Например, когда вы читаете «Microsoft .NET: Architecting Applications for the Enterprise» Дино Эспозито, вы заметите, что Dino считает, что обычно они приносят много накладных расходов, когда вы переносите объекты между слоями одного и того же AppDomain. Хотя он прав в этом, я все же нашел их полезными в этом конкретном сценарии, и я вижу, что накладные расходы становятся меньше, когда технология улучшается ... – Steven
Например, новые конструкторы языка C#, такие как автоматические свойства, упрощают определение DTO и инструмент рефакторинга, такой как Refactor! Pro позволяет автоматически генерировать DTO из анонимного определения типа внутри запроса LINQ. – Steven