Выбрать главу

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

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

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

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

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

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

Рис. 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.