Литмир - Электронная Библиотека

Этот пример наглядно демонстрирует разнонаправленность двух механизмов LINQ для SQL и Entity Framework, которая позволяет им существовать параллельно: если вам нужно отображение баз данных SQL Server на классы вашей бизнес-логики, чтобы все "просто работало", необходима скоростная разработка, и к гибкости ORM не предъявляются повышенные требования, то LINQ для SQL вполне может стать тем инструментом, который полностью удовлетворит ваши потребности. В случае же, когда к структуре модели данных и возможностям ORM предъявляются серьезные требования, вроде потенциальной поддержки разнообразных источников данных, то среди двух описанных ранее технологий Entity Framework — это то, что вам нужно.

Принципы построения слоя доступа к данным

Грамотно построенное приложение состоит из множества слабосвязанных, легкозаменяемых частей. В этом смысле при построении слоя доступа к данным (рис. 3.4) можно определить следующие принципы:

□ для доступа к модели данных необходимо использовать механизм ORM, который предпочтительнее всего взять со стороны, но не реализовывать своими силами;

□ интеграция ORM в систему должна быть слабосвязанной, так, чтобы существовала безболезненная возможность перейти на другой вариант ORM;

□ вместе с моделью данных, которую предоставит ORM, необходимо реализовать хранилища и сервисы, которые будут предоставлять доступ к необходимым наборам данных и реализовывать добавление, видоизменение и удаление данных. Реализация слабосвязанных сервисов и хранилищ позволит скрыть подробности реализации доступа к данным, предоставляемых ORM, что даст большую гибкость слою доступа к данным и позволит исключить жесткую привязку, например, к LINQ-запросам для работы с ORM.

Asp.net mvc framework - img_19

Что дает такая схема, и зачем нужна дополнительная прослойка между ORM и бизнес-логикой приложения:

□ внедрение хранилища для типовых запросов, например, получения записи по идентификатору или получения всех заявок заказчика, позволит, с одной стороны, унифицировать получение таких данных, а с другой, сделает связь с ORM менее жесткой;

□ внедрение сервисов похоже на внедрение хранилищ, только реализующих логику управления данными. Например, хорошей практикой является определение сервиса, который добавляет, изменяет либо удаляет данные. Обращаясь к такому сервису, ваш код не будет зависеть от реализации функций, существующей ORM и даже базы данных;

□ внедрение такого рода инъекций кода отделяет бизнес-логику от ORM и уменьшает зависимость между ними, что позволит в дальнейшем без особых трудов использовать другой ORM, переписав для этого только хранилище и сервисы. В случае же прямой связи бизнес-логики с ORM, безболезненной замены ORM провести не удастся, т. к. придется инспектировать и переписывать весь написанный код.

Возможность замены источника данных

Для более наглядного примера потребности в промежуточном коде и необходимости предусматривать возможные изменения в работе с ORM приведем случай из практики одного из авторов книги. Компания, в которой он работал, разрабатывала крупный проект, работающий с использованием SQL Server 2000. После нескольких лет разработки и поддержки, заказчик пожелал сменить SQL Server на другую СУБД. В связи с тем, что код проекта был жестко завязан на особенностях SQL Server, такой переход стоил больших усилий всего персонала компании.

В то время еще не существовало LINQ для SQL, но даже если бы компания использовала этот ORM, ей все равно пришлось бы переписывать подавляющую часть кода работы с данными в связи с тем, что LINQ для SQL не поддерживает ничего, кроме SQL Server.

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

Реализация слоя данных

Создадим простейший слой для работы с базой данных, который отвечал бы всем нашим требованиям. Для начала возьмем простую структуру базы данных (рис. 3.5).

Asp.net mvc framework - img_20

Рис. 3.5. Структура базы данных

У нас есть три таблицы: заказчики, заказы и товары. Каждая из таблиц содержит набор свойственных ей полей, так в таблице Products (товары) есть поле isAvailible, которое показывает, доступен ли товар в настоящее время. У таблицы Orders (заказы) есть поля count — количество товара в штуках и orderDateTime — дата и время оформления заказа. Таблица Customers (заказчики) содержит информацию о заказчике.

Для реализации хранилищ и сервисов нам необходимы интерфейсы данных, определим их так, как показано в листинге 3.1.

Листинг 3.1. Интерфейсы данных

public interface ICustomer {

  Guid CustomerId { set; get; }

  string Name { set; get; }

  string Phone { set; get; }

  string Address { set; get; }

}

public interface IOrder {

  Guid OrderId { set; get; }

  Guid CustomerId { set; get; }

  Guid ProductId { set; get; }

  int Count { set; get; }

  DateTime OrderDateTime { set; get; }

}

public interface IProduct {

  Guid ProductId { set; get; }

  string Name { set; get; }

  bool IsAvailible { set; get; }

  bool Cost { set; get; }

}

Воспользуемся мастером создания модели LINQ для SQL, чтобы сгенерировать классы для работы с базой данных. Посмотрим на сгенерированный код для таблицы Customers (приведен только фрагмент кода):

public partial class Customer : INotifyPropertyChanging,

                                INotifyPropertyChanged

{

  private System.Guid _customerId;

  private string _name;

  private string _address;

  private string _phone;

  private EntitySet<Order> _Orders;

  public Customer()

  {

    // код

  }

  [Column(Storage="_customerId",

    DbType="UniqueIdentifier NOT NULL",

    IsPrimaryKey=true)] public System.Guid customerId {

      get { return this._customerId; }

      set { // код }

    }

  [Column(Storage="_name",

    DbType="NVarChar(250) NOT NULL", CanBeNull=false)]

  public string name {

    get { return this._name; }

    set { // код }

  }

  [Column(Storage="_address",

    DbType="NVarChar(1024) NOT NULL",

    CanBeNull=false)]

  public string address {

    get { return this._address; }

    set { // код }

  }

  [Column(Storage="_phone", DbType="NVarChar(250)")]

  public string phone {

    get { return this._phone; }

    set { // код }

  }

  [Association(Name="Customer_Order",

    Storage="_Orders", ThisKey="customerId",

    OtherKey="customerId")]

  public EntitySet<Order> Orders {

    get { return this._Orders; }

    set { this._Orders.Assign(value); }

  }

}

Полученный код примечателен тем, что класс Customer является partial-классом, а это значит, что мы можем легко расширить его, и все прочие классы, для поддержки наших интерфейсов. Создадим частичные классы для реализации интерфейсов на базе LINQ для SQL так, как показано в листинге 3.2.

13
{"b":"971383","o":1}