Статьи

Linq to SQL с WCF в многоуровневой среде — часть 1

Во многих местах, на форумах, блогах или технических беседах с некоторыми коллегами, я слышал, что некоторые городские легенды о Linq to SQL мне встречались:

  • Вы не можете реализовать многоуровневые приложения с Linq to SQL

  • Linq to SQL нельзя использовать для приложений уровня предприятия

Я не могу сказать, что оба эти утверждения являются особенно неправильными или правильными, конечно, Linq to SQL не может обрабатывать каждый сценарий, но, честно говоря, он обрабатывает большинство сценариев, иногда даже лучше, чем некоторые другие RAM- ориентированные ORM . В этой статье я создам симуляцию корпоративного веб-приложения с разделенными уровнями доступа к данным, службам и представлениям и позволю им взаимодействовать друг с другом (по крайней мере, от службы до пользовательского интерфейса) через WCF — Windows Communication Foundation ,

Это будет пара (может быть больше) сообщений, и это первая часть. Я отправлю пример кода со следующим постом.

Я должен сказать, что эта статья не является введением в Linq для SQL и в WCF, поэтому вам нужны базовые знания обоих миров, чтобы извлечь выгоду из этой смеси. Мы разработаем приложение шаг за шагом с простым сценарием, но у нас будут наиболее важные характеристики разобщенной (с точки зрения DataContext ) многоуровневой корпоративной архитектуры.

Поскольку эта архитектура является более масштабируемой и надежной, при ее реализации с использованием Linq to SQL необходимо учитывать некоторые приемы:

  • Наш DataContext будет мертвым большую часть времени. Поэтому мы не сможем использовать отслеживание объектов для генерации наших операторов SQL из коробки.

  • Это также приводит к тому, что мы должны знать, какие объекты следует удалять, что вставлять и что обновлять. Мы не можем просто «сделать это» и отправить изменения, как мы делаем это в подключенном режиме. Это означает, что мы должны поддерживать состояние объектов вручную (извините, ребята, я чувствую такую ​​же боль).

  • Передача данных по сети является еще одной проблемой, поскольку мы не пишем сущности самостоятельно (и в случае их изменения разработчик Linq to SQL может быть очень агрессивным), поэтому это приводит нас к общая ситуация

  • Мы можем создавать свои собственные объекты и писать переводчики для преобразования из Linq Entities в наши собственные.

  • Мы можем попытаться настроить сущности Linq так, как мы можем.

Поскольку первый вариант очевиден и его легко реализовать, мы пойдем по второму маршруту, чтобы изучить границы этой настройки.

Чтобы было яснее, что я буду делать, вот базовая, но функциональная схема результирующего n-уровневого приложения:

[img_assist | nid = 3448 | title = | desc = Рисунок 1 — Архитектурная схема примера приложения. | link = none | align = none | width = 271 | height = 323]

В нашем примере мы собираемся использовать Linq to SQL в качестве ORM Mapper. Итак, как вы видите в схеме, Linq to SQL не дает нам возможности вообще не писать DAL-слой. Но это уменьшает как хранимые запросы / процедуры, так и количество отображений, которые мы должны были делать вручную ранее.

Разработка приложения

сценарий

Сценарий, который я придумал, — это веб-сайт избранного, состоящий из двух простых страниц, позволяющих пользователям вставлять , удалять , обновлять и извлекать пользователей и их избранные по запросу. 1 пользователь может иметь много избранных.

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

дизайн

юридические лица

Вот диаграмма объекта сущностей; они такие же, как таблицы БД:

[img_assist | nid = 3449 | title = | desc = Рисунок 2. Диаграмма сущностей | link = none | align = none | width = 404 | height = 170]

Смотрите дополнительные поля «Версия» в сущностях; это тип Binary в .NET и TimeStamps в SQL Server 2005. Мы будем использовать их, чтобы позволить Linq to SQL решать проблемы параллелизма для нас.

Поскольку мы собираемся использовать веб-службу с помощью WCF, нам необходимо пометить наши объекты как DataContract, чтобы сделать их доступными для сериализации через DataContractSerializer . Мы можем сделать это, щелкнув правой кнопкой мыши на конструкторе и перейдя к свойствам, и изменив свойство Сериализация на однонаправленное, как показано на рисунке:

[img_assist | nid = 3450 | title = | desc = Рисунок 3. Окно свойств | ссылка = нет | выровнять = нет | ширина = 244 | высота = 238]

Сделав и сохранив это, мы увидим в файле designer.cs наши объекты, помеченные как DataContract, и члены как DataMember s.

Как упоминалось ранее, мы должны поддерживать наше состояние entites — чтобы знать, были ли они удалены, вставлены или обновлены. Для этого я собираюсь определить перечисление следующим образом:

/// <summary>
/// The enum helps to identify what is the latest state of the entity.
/// </summary>

public enum EntityStatus
{

/// <summary>
/// The entity mode is not set.
/// </summary>

None = 0,

/// <summary>
/// The entity is brand new.
/// </summary>

New = 1,

/// <summary>
/// Entity is updated. 
/// </summary>

Updated = 2,

/// <summary>
/// Entity is deleted. 
/// </summary>

Deleted = 3,

}

Мы собираемся иметь это поле в каждой сущности, поэтому давайте определим базовую сущность с этим полем в ней:

[DataContract]
public class BaseEntity

{

/// <summary>
/// Gets or sets the status of the entity.
/// </summary>
/// <value>The status.</value>

[DataMember]

public EntityStatus Status { get; set; }

}

И затем, все, что нам нужно сделать, это создать частичные классы для наших сущностей и расширить их из базовой сущности:

public partial class User : BaseEntity

{

}

public partial class Favorite : BaseEntity

{

}

Теперь наши объекты готовы безопасно путешествовать вместе со своим арсеналом.

Дизайн сервисного уровня

Поскольку мы собираемся использовать WCF, нам нужно иметь:

  • Сервисные контракты (интерфейсы)
  • Реализация услуг (конкретные классы)
  • Сервис Клиенты (Потребители)
  • Сервис Host (веб-сервис в нашем случае)

Контракты на обслуживание

У нас будет 2 сервиса: Сервис избранного и Сервис пользователей. У него будет 4 метода: 2 получения и 2 обновления. Мы будем выполнять вставку, обновление и удаление в зависимости от статуса, поэтому нет необходимости определять отдельные функции для всех. Вот договор для Пользователя:

 /// <summary>
/// Contract for user operations 
/// </summary>

[ServiceContract]

public interface IUsersService

{

/// <summary>
/// Gets all users.
 /// </summary>
/// <returns></returns>

[OperationContract]

IList<User> GetAllUsers();

/// <summary>
/// Updates the user.
/// </summary>
/// <param name=”user”>The user.</param>

[OperationContract]

void UpdateUser(User user);

/// <summary>
/// Gets the user by id.
/// </summary>
/// <param name=”id”>The id.</param>
/// <returns></returns>

[OperationContract]

User GetUserById(int id);

/// <summary>
/// Updates the users in the list according to their state.
/// </summary>
/// <param name=”updateList”>The update list.</param>

[OperationContract]

void UpdateUsers(IList<User> updateList);

}

А вот и контракт на услугу «Избранное»:

/// <summary>
/// Contract for favorites service
/// </summary>

[ServiceContract]

public interface IFavoritesService

{

// <summary>
/// Gets the favorites for user.
/// </summary>
/// <param name=”user”>The user.</param>
/// <returns></returns>

[OperationContract]

IList<Favorite> GetFavoritesForUser(User user);

/// <summary>
/// Updates the favorites for user.
/// </summary>
/// <param name=”user”>The user.</param>

[OperationContract]

void UpdateFavoritesForUser(User user);

}

Реализация услуг (конкретные классы)

Так как мы разрабатываем приложение БД без какой-либо бизнес-логики, разработчики уровня обслуживания довольно скромны и подлые. Вот реализация Сервиса для UserService:

[ServiceBehavior(IncludeExceptionDetailInFaults=true)]

public class UsersService : IUsersService

{

IUsersDataAccess DataAccess { get; set; }

public UsersService()

{

DataAccess = new UsersDataAccess();

}

#region IUsersService Members

/// <summary>
/// Gets all users.
/// </summary>
/// <returns></returns>

[OperationBehavior]

public IList<User> GetAllUsers()

{

return DataAccess.GetAllUsers();

}

/// <summary>
/// Updates the user.
/// </summary>
/// <param name=”user”>The user.</param>

[OperationBehavior]

public void UpdateUser(User user)

{

DataAccess.UpdateUser(user);

}

/// <summary>
/// Gets the user by id.
/// </summary>
/// <param name=”id”>The id.</param>
/// <returns></returns>

[OperationBehavior]

public User GetUserById(int id)

{

return DataAccess.GetUserById(id);

}

/// <summary>
/// Updates the users in the list according to their state.
/// </summary>
/// <param name=”updateList”>The update list.</param>

[OperationBehavior]

public void UpdateUsers(IList<User> updateList)

{

DataAccess.UpdateUsers(updateList);

}

#endregion

}

И, как вы можете себе представить, реализация любимого сервиса почти такая же.

Это было достаточно долго, поэтому давайте сократим это здесь. В следующем посте я расскажу о реализации представления, обслуживания и уровня данных. Таким образом, мы увидим, как лучше всего подойти к изменению этих объектов в сетке данных, передать их через прокси WCF и зафиксировать изменения (вставить, обновить, удалить) в базу данных SQL 2005. Я также предоставлю исходники со следующим постом. Оставайтесь с нами до тех пор.