Рекомендации по использованию веб-приложений LINQ to SQL

По моему опыту создания веб-приложений, я всегда использовал подход n-уровня. DAL, который получает данные из db и заполняет объекты, и BLL, который получает объекты из DAL и выполняет любую требуемую бизнес-логику, и веб-сайт, на котором он отображает данные из BLL. Недавно я начал изучать LINQ, и большинство примеров показывают запросы, происходящие прямо из кода кода Web-приложения (возможно, я видел только слишком упрощенные примеры). В n-уровневых архитектурах это всегда считалось большим нет-нет.
Я немного не знаю, как создать новое веб-приложение. Я использовал конструктор Server Explorer и dbml в VS2008 для создания связей dbml и объектов. Мне кажется немного непонятным, если dbml будет считаться слоем DAL, если веб-сайт должен вызывать методы в BLL, который затем будет выполнять запросы LINQ и т.д.
Каковы некоторые общие рекомендации по архитектуре или подходы к созданию решения для веб-приложений с использованием LINQ to SQL?

Ответы

Ответ 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 выполнять полную операцию в базе данных.

Надеюсь, это даст некоторые идеи о том, что вы можете сделать. Конечно, это мой взгляд на предмет и то, что я нашел практичным в моих ситуациях.

Ответ 2

LINQ to SQL - это доступ к БД в реализации DAL, если вы хотите разделить между DAL и BLL. Если у вас менее сложное веб-приложение (а также никогда не предназначено для переключения бэкэндов БД), вы можете уйти без явного DAL/BLL и сделать все в коде. LINQ to SQL отлично подходит для операций readonly, но для выполнения операций записи немного больше работы.