Ответ 1
Боюсь, вы действительно видели слишком упрощенные примеры. LINQ to SQL (System.Data.Linq) - это ваш уровень DAL. Классы L2S генерируют ваш домен (но не путать с Domain-Driven Design). Кроме того, вы все равно можете написать свой бизнес-уровень.
Я всегда стараюсь предотвратить утечку LINQ to SQL DataContext
в уровень представления (ваше веб-приложение). Поэтому он не должен создавать или фиксировать DataContext
. Также вы не должны возвращать объекты IQueryable<T>
к уровню представления. IMO бизнес-уровень должен иметь полный контроль над временем жизни DataContext
(единица работы) и форму SQL-запросов.
Однако есть несколько вариантов. Некоторые люди стремятся ослабить эти ограничения. Другие даже идут намного дальше. Это зависит от вашего вкуса и размера приложения. Чем больше приложение, тем больше оправдано добавление слоев абстракции.
При отказе от IQueryable
и других данных, связанных с оставлением бизнес-уровня, у вас возникнут некоторые интересные проблемы. Например, уровень представления должен проинструктировать бизнес-уровень, как сортировать результаты. Хотя вы можете позволить слою представления сортировать результаты сами, это означало бы, что вам нужно будет получить все данные из базы данных и страницы на уровне презентации, что приведет к очень плохо исполняющейся системе. Существует несколько решений этой проблемы. Во всех случаях вам нужно будет сообщить бизнес-уровню, как сортировать результаты для вас. Решения можно найти здесь, когда вы ищете динамическую сортировку LINQ. Я сам написал такое решение, здесь.
Еще одна проблема, которая запрещает IQueryable
оставлять ваш BL, заключается в том, что также объекты домена часто не могут оставить ваш BL. Большинство объектов домена LINQ to SQL будут содержать ленивые загруженные свойства (например, коллекции для других объектов домена). Однако, когда DataContext
контролирует бизнес-уровень, он будет удален до того, как вы вернете результаты на уровень представления. Когда презентация, чем доступ к ленивому загруженному свойству, возникает исключение, поскольку DataContext
уже удален. Когда вы размещаете DataContext
в своем бизнес-слое, это поведение, конечно, "по дизайну". Разрешение слоя представления на получение ленивых загружаемых свойств означает, что BL теряет контроль над запросами, которые отправляются в базу данных, тем самым теряя контроль над производительностью.
Чтобы устранить эту проблему, вы должны вернуть объекты передачи данных (DTO) из BL в уровень презентации. DTO будет содержать только данные, а не внутренние DataContext
, и никакие ленивые загруженные свойства. DTO может быть специально отформатирован для фактического запроса. Конечно, DTO приводят к чрезмерной нагрузке на кодирование, поэтому размер вашей системы и потребности в производительности должны ее оправдать. Чтобы облегчить для себя, я склонен ставить методы статической проекции на DTO. Хотя это не соответствует принципу разделение проблем, я считаю это очень практичным решением. Найдите экземпляр в этом 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
DTO. Это значительно упрощает преобразование объектов домена в 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 выполнять полную операцию в базе данных.
Надеюсь, это даст некоторые идеи о том, что вы можете сделать. Конечно, это мой взгляд на предмет и то, что я нашел практичным в моих ситуациях.