412 000 произведений, 108 200 авторов.

Электронная библиотека книг » Гайдар Магдануров » Asp.net mvc framework » Текст книги (страница 4)
Asp.net mvc framework
  • Текст добавлен: 8 июля 2026, 17:38

Текст книги "Asp.net mvc framework"


Автор книги: Гайдар Магдануров



сообщить о нарушении

Текущая страница: 4 (всего у книги 17 страниц)

После реализации интерфейсов создадим простейшие хранилища и сервисы, для этого сначала объявим их интерфейсы:

public interface ICustomerRepository {

  ICustomer GetCustomerById(Guid customerId);

  IEnumerable GetCustomersByProduct(Guid productId);

}

Хранилище для заказчиков позволит выбирать заказчика по идентификатору и выбирать всех заказчиков, связанных с определенным товаром.

public interface IOrderRepository {

  IOrder GetOrderById(Guid orderId);

  IEnumerable GetCustomerOrders(Guid customerId);

}

Хранилище для заказов позволит выбирать заказ по идентификатору и список заказов определенного заказчика.

public interface IProductRepository {

  IProduct GetProductById(Guid productId);

  IEnumerable GetAvailableProducts();

  IEnumerable GetProductListByName(string name);

}

Хранилище для товаров позволит найти товар по идентификатору, список товаров по наименованию и список товаров, которые доступны в данный момент.

Реализация данных хранилищ не составляет труда (листинг 3.3).

Листинг 3.3. Реализация хранилищ

public class CustomerRepository : ICustomerRepository {

  private readonly MyDatabaseDataContext _dataBase;

  public CustomerRepository(MyDatabaseDataContext db)

  {

    if (db == null)

      throw new ArgumentNullException("db");

    _dataBase = db;

  }

  public ICustomer GetCustomerById(Guid customerId)

  {

    if (customerId == Guid.Empty)

      throw new ArgumentException("customerId");

    return _dataBase.Customers

       .SingleOrDefault(x => x.customerId == customerId);

  }

  public IEnumerable GetCustomersByProduct(Guid productId) {

    if (productId == Guid.Empty)

      throw new ArgumentException("customerId");

    return _dataBase.Orders

      .Where(x => x.productId == productId)

      .Select(x => x.Customer).Distinct();

  }

}

public class OrderRepository : IOrderRepository {

  private readonly MyDatabaseDataContext _dataBase;

  public OrderRepository(MyDatabaseDataContext db)

  {

    if (db == null)

      throw new ArgumentNullException("db");

    _dataBase = db;

  }

  public IOrder GetOrderByld(Guid orderld)

  {

    if (orderId == Guid.Empty)

      throw new ArgumentException("orderId");

    return _dataBase.Orders

      .SingleOrDefault(x => x.orderId == orderId);

  }

  public IEnumerable GetCustomerOrders(Guid customerId)

  {

    if (customerId == Guid.Empty)

      throw new ArgumentException("customerId");

    return _dataBase.Orders

      .Where(x => x.customerId == customerId)

      .Select(x => x);

  }

}

public class ProductRepository : IProductRepository {

  private readonly MyDatabaseDataContext _dataBase;

  public ProductRepository(MyDatabaseDataContext db)

  {

    if (db == null)

      throw new ArgumentNullException("db");

    _dataBase = db;

  }

  public IProduct GetProductById(Guid productId)

  {

    if (productId == Guid.Empty)

      throw new ArgumentException("productId");

    return _dataBase.Products

      .SingleOrDefault(x => x.productId == productId);

  }

  public IEnumerable GetAvailableProducts()

  {

    return _dataBase.Products.Where(x => x.isAvailable)

      .Select(x => x);

  }

  public IEnumerable GetProductListByName(string name)

  {

    if (string.IsNullOrEmpty(name))

      throw new ArgumentException("name");

    return _dataBase.Products

      .Where(x => x.name.Contains(name))

      .Select(x => x);

  }

}

Далее создадим сервисы, которые будут помогать нам манипулировать данными. Сначала определим интерфейсы сервисов:

public interface ICustomerService {

  ICustomer Create(string name, string phone, string address);

  void Delete(ICustomer customer);

  void Update(ICustomer customer, string name,

             string phone, string address);

}

public interface IOrderService {

  IOrder Create(ICustomer customer, IProduct product, int count,

      DateTime orderDateTime);

  void Delete(IOrder order);

  void Update(IOrder order, ICustomer customer,

      IProduct product, int count, DateTime orderDateTime);

}

public interface IProductService {

  IProduct Create(string name, bool isAvailable, decimal cost);

  void Delete(IProduct product);

  void Update(IProduct product, string name,

          bool isAvailable, decimal cost);

Реализуем интерфейсы сервисов, как показано в листинге 3.4, для класса customerService. Чтобы уменьшить количество кода, приводить реализацию для других классов мы не будем, для остальных классов определяем методы Create, Delete и Update точно таким же образом.

Листинг 3.4. Реализация интерфейсов сервисов

public class CustomerService : ICustomerService {

  private readonly MyDatabaseDataContext _database;

  public CustomerService(MyDatabaseDataContext db)

  {

    if (db == null)

      throw new ArgumentNullException("db");

    _database = db;

  }

  public ICustomer Create(string name, string phone, string address)

  {

    if (String.IsNullOrEmpty(name))

      throw new ArgumentNullException("name");

    Customer customer = new Customer()

             {

               CustomerId = Guid.NewGuid(),

               Address = address,

               Phone = phone,

               Name = name

             };

    _database.Customers.InsertOnSubmit(customer);

    return customer;

  }

  public void Delete(ICustomer customer)

  {

    if (customer == null)

      throw new ArgumentNullException("customer");

    _database.Customers.DeleteOnSubmit((Customer)customer);

  }

  public void Update(ICustomer customer, string name,

                     string phone, string address)

  {

    if (customer == null)

      throw new ArgumentNullException("customer");

    if (String.IsNullOrEmpty(name))

      throw new ArgumentNullException("name");

    customer.Name = name;

    customer.Phone = phone;

    customer.Address = address;

  }

}

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

public class UnitOfWork : IDisposable {

  private readonly MyDatabaseDataContext _database;

  public MyDatabaseDataContext DataContext {

    get

    {

      return _database;

    }

  }

  private bool _disposed;

  public UnitOfWork()

  {

    _database = new MyDatabaseDataContext();

  }

  public void Commit()

  {

    _database.SubmitChanges();

  }

  public void Dispose()

  {

    Dispose(true); GC.SuppressFinalize(this);

  }

  private void Dispose(bool disposing) {

    if (!this._disposed)

    {

      if (disposing)

      {

        _database.Dispose();

      }

      _disposed = true;

    }

  }

}

Этот класс реализует паттерн UnitOfWork, который описывает реализацию атомарного действия, в данном случае создание контекста базы ORM, использование, сохранение изменений и разрушение объекта.

Пример использования слоя данных

Наша инъекция кода полностью реализована, рассмотрим вариант использования:

using (UnitOfWork unitOfWork = new UnitOfWork())

{

  ICustomerService customerService = new

    CustomerService(unitOfWork.DataContext);

  IOrderService orderService = new

    OrderService(unitOfWork.DataContext);

  IProductService productService = new

    ProductService(unitOfWork.DataContext);

  ICustomer customer = customerService.Create("Hoвый заказчик",

        "111-22-33", "Адрес нового заказчика");

  IProduct product = productService.Create("Новый товар", true, 50000);

  orderService.Create(customer, product, 200, DateTime.Now);

  unitOfWork.Commit();

}

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

Еще одним хорошим ходом могло бы стать вынесение класса UnitofWork и контекста ORM в отдельные слабосвязанные сущности, для того чтобы хранилища и сервисы не были завязаны на определенные контексты ORM. Но в связи с тем, что хранилища и сервисы, по своей сути, жестко связаны с конкретным ORM, реализация такого вынесения требуется редко.

Механизмы для работы с данными


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

XML-данные

.NET Framework предлагает широкий ассортимент объектов для доступа к XML-данным, которые расположены в стандартных сборках: System.Xml, System.Xml.XPath, System.Xml.Xsl, System.Xml.Schema и System.Xml.Linq. Рассмотрим назначение и наиболее полезные классы каждой из сборок:

□ System.Xml – содержит базовые классы для работы с XML, такие как

XmlDocument, XmlElement, XmlNode и множество других, которые позволяют реализовать загрузку из файлов, обработку, добавление, изменение, удаление XML-данных;

□ System.Xml.XPath – содержит классы для реализации работы механизма XPath, позволяющего писать выражения к XML-документу для поиска необходимых данных;

□ System.Xml.Xsl – содержит классы для поддержки реализации XSLT-преобразований XML-документа;

□ System.Xml.Schema – содержит классы для поддержки XSD-схем и валидации XML-данных на их основе. Содержит большое число классов, позволяющих создавать XSD-схемы и использовать их;

□ System.Xml.Lin –  последняя сборка, которая недавно появилась в .NET Framework. Содержит классы, реализующие механизм доступа к XML-данным на основе LINQ-выражений. Этот механизм носит собственное название LINQ для XML. Он позволяет использовать уже известные вам LINQ-выражения для обработки XML-данных.

Рассмотрим пример обработки XML-данных с помощью LINQ для XML. Предположим, что у нас есть следующий XML-файл:

 

   

      Владимир Иванов

      yn. CTpoMTanefr

      12

      23

      MocKBa

      100888

      Россия

   

 

 

   

      Сергей Петров

      ул. Бажова

      76

      123

      Eкатеринбург

      620000

      Россия

   

 

Для получения имени по номеру ордера, используя LINQ для XML, мы можем написать следующий код:

XDocument xdoc =

XDocument.Load("D:\CPS\#Projects\MVCBook\MVCBook\Order.xml");

IEnumerable orders =

  xdoc.Element("Orders").Descendants("Order");

int orderId = 100;

IEnumerable order =

  orders.Where(x => x.Attribute("OrderId").Value == orderId.ToString());

XElement address = order.Select(x => x.Element("Address"))

  .FirstOrDefault();

string name = address.Element("Name").Value;

В данном примере загружается файл orders.xml, в переменной orders присваиваются все заявки (элементы Order в XML-файле). Затем с помощью LINQ-запроса находится заявка с идентификатором 100. После этого выбирает элемент адреса заявки и, в завершение, из адреса извлекается имя.

Стоит отметить, что в данном примере не производились проверки возвращаемых значений на null, что на практике делать обязательно.

Работа с данными через ADO.NET

Понятие ADO.NET на платформе .NET достаточно широкое. На самом деле LINQ для SQL и LINQ для XML, а также все остальные стандартные LINQ-реализации входят в механизм ADO.NET как составные части. Но исторически ADO.NET развивалось от версии к версии платформы через другие механизмы, такие как объекты Dataset.

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

Практически весь функционал по работе с данными через Dataset расположен в .NET Framework в пространствах имен System.Data и System.Data.OleDb (кроме этого, существует еще более двух десятков пространств имен, название которых начинается с System.Data). Перечислим основные самые важные классы, которые используются чаще всего при работе с Dataset из System.Data:

□ DataSet, DataTable, DataColumn, DataRow – различные варианты представления данных (набор, таблица, схема колонки, строка данных);

□ ConstraintCollection, Constraint, UniqueConstraint – представляют ограничения, которые могут быть наложены на объекты DataColumn.ConstraintCollection содержит набор таких ограничений для объекта DataTable;

□ DataView – объект, позволяющий привязывать данные из DataTable к формам WinForms или WebForms;

Список наиболее часто используемых классов пространства имен System.Data.OleDb:

□ OleDbConnection – обеспечивает подключение к базе данных через механизм OLE DB;

□ oleDbCommand – содержит запрос к базе данных на языке SQL либо представляет хранимую процедуру;

□ oleDataAdapter – обеспечивает заполнение объекта DataSet нужными данными с помощью элементов OieDbCommand;

□ oleDbDataReader – позволяет читать данные, полученные от oieDataAdapter, в виде строк и в прямом порядке;

□ oleDbTransaction – представляет собой транзакцию в источнике данных.

Существует еще одно часто используемое пространство имен, которое может быть полезно при разработке баз данных на SQL Server – System.Data.SqlClient. Это пространство имен содержит весь функционал System.Data.OleDb, только в реализации для использования исключительно с SQL Server. Применение этого пространства имен позволяет получить доступ к особенностям SQL Server, таким как новые типы данных SQL Server 2008.

Рассмотрим пример доступа к данным посредством базовых механизмов ADO.NET DataSet:

SqlConnection conn = new

  SqlConnection("Data Source=localhost,

      Initial Catalog=BookMVC, Integrated Security=True");

conn.Open();

SqlCommand cmd = new SqlCommand("SELECT * FROM Customers", conn);

SqlDataReader reader = cmd.ExecuteReader();

string phone;

while (reader.Read())

{

  string name = reader["name"].ToString();

  if (name == "Сергей Петров")

  {

    phone = reader["phone"].ToString();

    break;

  }

}

reader.Close();

conn.Close();

Данный пример инициализирует строку соединения с базой данных и открывает соединение. Затем создает команду в виде SQL-запроса на чтение всех записей из таблицы Customers (заказчики). С помощью ExecuteReader команда выполняется, и для работы с данными строится объект SqlDataReader. После этого формируется цикл, который проходит по всем записям, ищет первую запись с именем Сергей Петров и прерывает цикл, сохраняя данные о телефоне в локальной переменной.

Для сравнения перепишем этот простой пример, используя LINQ для Dataset:

SqlConnection conn = new SqlConnection(@"

Data Source=localhost;

Initial Catalog=BookMVC;

Integrated Security=True");

conn.Open();

SqlCommand cmd = new SqlCommand("SELECT * FROM Customers", conn);

DataSet ds = new DataSet();

SqlDataAdapter adapter = new SqlDataAdapter(cmd);

adapter.Fill(ds);

DataTable customers = ds.Tables[0];

string phone = customers. AsEnumerable()

  .Where(x => x.Field("name") == "Сергей Петров")

  .Select(x => x.Field("phone")).SingleOrDefault();

conn.Close();

Как нетрудно заметить, наш цикл, нацеленный на поиск данных, исчез, и ему на замену пришло LINQ-выражение, которое выполняет точно ту же логику – ищет телефон по определенному номеру.

Механизм доступа к данным через ADO.NET Dataset в общем случае производительнее, чем через ORM, вроде LINQ для SQL или Entity Framework, поскольку при работе с объектами типа Dataset нет затрат на реализацию объектной модели базы данных. Работа с данными происходит напрямую через SQL-запросы или вызов хранимых процедур. Разработчик сам контролирует весь процесс получения и использования данных, что дает больше возможностей и прирост производительности, но с другой стороны, увеличивает объем написания необходимого кода.

Поскольку основная часть времени при запросе тратится на его выполнение, то использование ADO.NET вместо популярных ORM для доступа к данным оправдано только там, где ставятся повышенные требования к потреблению памяти и производительности. В большинстве же случаев затраты на ORM окупаются скоростью и простотой разработки, единой моделью доступа к данным и меньшему количеству кода.

LINQ для SQL



Работа с LINQ для SQL на платформе .NET Framework осуществляется с помощью классов пространства имен System.Linq и System.Data.Linq.

Перечислим основные классы этих пространств имен.

System.Linq:

• Enumerable – предоставляет набор статичных методов для работы с объектами, реализующими интерфейс IEnumerable.

• Queryable – предоставляет набор статичных методов для работы с объектами, реализующими интерфейс IQueryable.

Методы этих классов, вроде Where, Select, Sum и др., используются в любом LINQ-выражении для построения запросов, обработки и фильтрации данных.

System.Data.Linq:

• DataContext – основной объект для работы с LINQ для SQL, предоставляет контекст базы данных, через который осуществляется доступ ко всем сущностям базы данных;

• EntitySet, EntityRef (структура) – обеспечивают связь между сущностями в LINQ для SQL;

• Table – представляет таблицу с возможностью изменения объектов;

• CompiledQuery – предоставляет возможность компилировать и повторно использовать запросы.

Контекст базы данных, наследующий от класса DataContext, в LINQ для SQL принято создавать с помощью мастера, который автоматически сгенерирует LINQ для SQL-классов. После такой генерации работа с объектной моделью становится очень простой, например, приведенный в разделе ADO.NET пример в исполнении LINQ для SQL будет выглядеть так:

using (MyDatabaseDataContext dataContext = new MyDatabaseDataContext())

{

  string phone = dataContext.Customers

    .Where(x => x.name == "Сергей Петров")

    .FirstOrDefault().phone;

}

Согласитесь, это заметно более простое решение по сравнению с вариантом, написанным с использованием ADO.NET Dataset.

Entity Framework

Entity Framework можно использовать только на .NET Framework версии 3.5 с установленным пакетом обновления SP1. Для работы с Entity Framework предлагаются следующие пространства имен: System.Data.Entities, System.Data.Objects, System.Data.EntityClient и др.

В отличие от LINQ для SQL модель данных Entity Framework (EDM) состоит из трех частей:

концептуальная модель (CSDL) – позволяет создавать сущности, не равнозначные сущностям базы данных, например, комплексные сущности, состоящие из элементов нескольких таблиц, или сущности, наследующие от других сущностей;

модель хранения данных (SSDL) – определяет логическую модель базы данных;

модель сопоставления хранения данных и концептуальной модели (MSL) – определяет, как логическая модель хранения базы данных сопоставляется с концептуальной моделью.

При работе с Entity Framework в Visual Studio 2008 SP1 предлагается мастер автоматического создания модели на базе заданных объектов базы данных. Вы можете использовать его для генерации всех трех частей EDM. Результатом работы мастера станет файл *.edmx, который будет содержать все три модели сразу. Контекст базы данных и отображение на классы C# будут сгенерированы в другой файл *.Designer.cs.

После создания мастером модели EDM вы сможете манипулировать концептуальной моделью, моделью хранения и моделью сопоставления с помощью специального визуального дизайнера Entity Framework. Этот дизайнер, кроме всего прочего, позволяет выполнять и такие операции, как валидацию модели EDM, обновление модели из базы данных.

Другим отличием Entity Framework от LINQ для SQL является разнообразие доступа к модели данных. Существует три варианта работы с EDM:

LINQ для сущностей – аналог LINQ для SQL с полной поддержкой всех особенностей Entity Framework;

Entity SQL – особенный язык, диалект SQL, который служит для работы с моделью EDM. Имеет ряд отличий и ограничений по сравнению с обычным SQL;

□ третий вариант совмещает в себе первые два, с помощью LINQ-выражений можно строить запросы на языке Entity SQL.

Подробное описание особенностей Entity Framework или Entity SQL выходит за рамки этой книги. Здесь мы приведем только очевидные отличия LINQ для сущностей от LINQ для SQL:

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

□ отсутствие в LINQ для сущностей поддержки методов Single и singleOrDefault, вместо которых рекомендуется использовать First и FirstOrDefault;

□ вместо методов DeleteOnSubmit и SubmitChanges в LINQ для сущностей предложены методы DeleteObject и saveChanges соответственно;

□ вместо метода XXX.InsertOnSubmit предложены обертки (автогенерируемые) AddToXXX, где XXX – это имя отображаемой сущности (таблицы).

Еще одно незначительное отличие в процессе автогенерации моделей с помощью мастеров в Visual Studio представляет собой изменение имени таблицы при отображении на класс в LINQ для SQL. Например, таблица Customers отобразится на класс Customer, без последней буквы "s". При создании классов в Entity Framework мастер не производит такие изменения.

NHibernate

Еще одним вариантом организации доступа к данным может стать популярная ORM-библиотека NHibernate – довольно старый механизм, портированный на платформу .NET Framework из Java-проекта Hibernate. Как ORM, Hibernate давно заслужила право называться зрелой, гибкой, мощной и главное производительной библиотекой. Адаптация под .NET, хотя и не совсем полностью реализует функционал Hibernate версии 3, но предлагает все тот же механизм, сравнимый по мощности и производительности с предком.

NHibernate, как и другие ORM, использует для реализации доступа к данным мэппинг в виде XML-файла. Этот файл должен быть оформлен согласно схеме nhibernate-mapping.xsd, которая идет в комплекте поставки. По традиции названия всех таких файлов мэппинга формируются как class_name.hbm.xml. Такой файл может выглядеть примерно так:

  namespace="MyNamespace" assembly="MyNamespace">

 

   

   

 

 

 

 

Здесь создается мэппинг класса Customer на таблицу Customers в базе данных, которая содержит ряд полей: customerId, name, phone, address. Для подключения к базе данных должен быть создан другой конфигурационный файл, похожий на этот:

 

   

      NHibernate.Connection.DriverConnectionProvider

    

   

      NHibernate.Driver.SqlClientDriver

    

   

      NHibernate.Dialect.MsSql2005Dialect

    

   

      Server=(local);

      Initial Catalog=MyDatabase;

      Integrated Security=SSPI;

    

   

       assembly="MyNamespace" />

 

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

□ Fluent NHibernate (http://fluentNHibernate.org/);

□ MyGeneration (http://www.mygenerationsoftware.com/portal/default.aspx);

□ NHibernate 1.2 Generator (http://gennit.com/);

□ NHibernate Query Generator

(http://ayende.com/projects/downloads/NHibernate-query-generator.aspx);

□ NHibernate Entity Generator (http://www.softpedia.com/get/Programming/Other-Programming-Files/NHibernate-Entity-Generator.shtml);

□ NHibernate Helper Kit (http://www.codeproject.com/KB/dotnet/NHibernate_Helper_Kit.aspx);

□ многие другие (http://stackoverflow.com/questions/41752/nhibernate-generators).

Для работы с NHibernate используются пространства имен NHibernate и NHibernate.Cfg. Второе служит для загрузки файла конфигурации, например, как показано в следующем примере:

ISessionFactory sessionFactory = new Configuration()

  .Configure("Nhibernate.cfg.xml").BuildSessionFactory();

где Nhibernate.cfg.xml – ваш файл конфигурации.

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

ISession session;

ITransaction tran;

try

{

  session = sessionFactory.OpenSession();

  tran = session.BeginTransaction();

  // делаем какую-то работу

  tran.Commit();

  session.Close();

}

catch (Exception ex)

{

  tran.Rollback();

  session.Close();

}

Как вы можете заметить, NHibernate реализует механизм транзакций с возможностью откатывать ненужные изменения.

Работа с объектами NHibernate возможна с помощью одного из трех вариантов:

Hibernate Query Language (HQL) – языка во многом похожего на язык LINQ c SQL-синтаксисом;

Query By Criteria (QBC) – объектный вариант, который позволяет строить выражения запросов динамически через объектно-ориентированный API;

□ через обыкновенные SQL-запросы.

Приведем простой пример использования HQL:

string result = (string)session.createQuery(@"select phone

         from Customer as c

         where c.name = 'Сергей Иванов'").UniqueResult();

Пример вернет телефон заказчика по его имени. Перепишем этот пример с использованием Query By Criteria:

ICriteria crit = session.CreateCriteria(typeof(Customer));

crit.Add(Expression.Eq("name", "Сергей Иванов"));

crit.SetMaxResults(1);

string phone = (string) crit.UniqueResult();

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

Кроме того, в будущих версиях NHibernate может появиться полноценная поддержка LINQ. По крайней мере, частичная реализация уже существует в NHibernate Contrib (https://nhcontrib.svn.sourceforge.net/svnroot/nhcontrib/trunk/). Эта возможность позволит оперировать NHibernate-объектами в привычном для LINQ-разработчиков стиле и, наверняка, увеличит привлекательность библиотеки.

Сравнение механизмов доступа к данным

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

□ удобство и единообразие доступа к данным;

□ объем кода, который может стать источником ошибок;

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

И, хотя понятие простоты субъективно, все же мы можем попытаться оценить описанные технологии по простоте работы:

□ на первом месте LINQ для SQL, как простой, но все-таки эффективный Framework для отображения структуры базы данных на код;

□ NHibemate и Entity Framework на втором месте по простоте, как механизмы схожие во многом с LINQ для SQL, но все-таки в силу своей комплексности и обширным возможностям более сложны при построении слоя доступа к данным;

□ более сложным вариантом построения механизма доступа к данным является использование ADO.NET либо других методов, вроде прямого доступа к XML-файлам. Этот вариант требует поддержки большого объема самописного кода, большого внимания к его написанию, он потенциально более незащищен в связи с возможными уязвимостями.

Рекомендации по выбору механизма доступа к данным

Используйте низкоуровневые механизмы, вроде ADO.NET, в тех проектах, где скорость доступа к данным – это основная задача, и главное требование к проекту – высокая производительность. Для проектов, ориентированных на SQL Server, которые не предполагают высоконагруженной работы с данными или не содержат сложной структуры базы данных, вполне возможно использовать LINQ для SQL. В случае, когда простоты LINQ для SQL не хватает, либо используется база данных, отличная от SQL Server, хорошим решением станет один из ORM Entity Framework или NHibernate, в зависимости от ваших пристрастий и предпочтений.

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

ГЛАВА 4

Контроллеры


В паттерне MVC контроллеры выполняют следующие последовательные функции:

□ контроллер реагирует на действия клиента, например: нажатие кнопки отправки формы на сервер, Ajax-запросы, которые генерирует браузер, и др.;

□ контроллер оперирует с моделью, изменяя ее состояние;

□ контроллер выбирает представление, которому передает модель данных, тем самым представление формируется контроллером и отображается пользователю либо передается в виде набора данных клиентскому программному обеспечению.

Назначение контроллеров

Обзор контроллеров в ASP.NET MVC

Для разработчика ASP.NET MVC-контроллер представляет собой класс, унаследованный от базового класса Controller (который в свою очередь унаследован от класса ControllerBase, реализующего интерфейс IController). Каждый файл контроллера MVC Framework должен подчиняться следующим требованиям:


    Ваша оценка произведения:

Популярные книги за неделю