Oct 6, 2009

NHibernate для начинающих. Часть 1.

Линки на остальные статьи

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

Начнем с того что где можно скачать и почитать.

· http://nhforge.org/ - сайт NHibnerate. Там можно найти википедии по многим вещам связанным NHibernate, википедию, ссылки на гугл группы, блоги и т.д.

· http://sourceforge.net/projects/nhibernate/files/NHibernate/ - тут можно скачать бинарники и исходники NHibernate.

· http://www.manning.com/kuate/ - книга по NHibernate. Если постараться можно найти эл. вариант.

· http://ayende.com/Blog/archive/2006/10/27/HowToGetStartedWithNHibernate.aspx - набор ссылок на статьи по NHibernate для начинающих

· http://jasondentler.com/blog/2009/09/part-8-daos-repositories-or-query-objects - там вверху есть ссылки на предидущие этой статьи. В них описываются часто возникаемые вопросы, такие как управление транзакциями, реализация паттерна репозиторий и прочее.

Итак начнем. Для демонстрации возможностей создадим простую ASP.NET страницу, с помощью которой можно будет просматривать, редактировать и удалять записи из базы данных.

Все операции в NHibernate выполняются через ISession. Экземпляр этого объекта можно получить через ISessionFactory. Создание объекта ISession очень быстро, в отличии от ISessionFactory поэтому ISessionFactory должен создаваться один раз и использоваться как Singleton объект на протяжении работы приложения .

Пусть мы хотим отобразить список товаров. Создадим в базе данных следующую таблицу:

products table

Скрипт создания:

CREATE TABLE [dbo].[Products](
[Id] [int] IDENTITY(1,1) NOT NULL,
[Name] [nvarchar](50) NOT NULL,
[Description] [nvarchar](250) NULL,
[Price] [float] NOT NULL,
CONSTRAINT [PK_Products] PRIMARY KEY CLUSTERED
(
[Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
ON [PRIMARY]

И создадим класс, который будет отвечать одной записи в этой базе данных. Для этого добавляем новый проект. Пусть он называется InheritanceCore (этот проект я буду использовать для дальнейших постов). Содержимое класса Product:

public class Product
{
public virtual int Id { get; protected set; }
public virtual string Name { get; set; }
public virtual string Description { get; set; }
public virtual double Price { get; set; }
}

Обратите внимание на 2 вещи:

1. Все поля должы быть virtual – это требование необходимо для lazy load и для отслеживания NHibnerate’ом всех изменений в объектах.

2. Поле Id содержит protected сеттер. Это сделано для того, чтобы никто случайно не заменил id объекта. Немного информации по этому поводу можно получить тут.

Для того, чтобы получить объект ISessionFactory добавим файл Global.asax. Его код должен выглядеть следующим образом:

public static ISessionFactory SessionFactory = CreateSessionFactory();
protected static ISessionFactory CreateSessionFactory()
{
return new Configuration().Configure(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "hibernate.config")).BuildSessionFactory();
}
public static ISession CurrentSession
{
get { return (ISession)HttpContext.Current.Items["current.session"]; }
set { HttpContext.Current.Items["current.session"] = value; }
}
protected void Application_BeginRequest(object sender, EventArgs e)
{
CurrentSession = SessionFactory.OpenSession();
}
protected void Application_EndRequest(object sender, EventArgs e)
{
if (CurrentSession != null)
CurrentSession.Dispose();
}

Для компиляции кода нужно добавить ссылки на все dll, находящиеся в папке Required_Bins скачаного Nhibernate contrib.

Тут происходит следующее. Поскольку поле SessionFactory статическое, то инициализируется оно только один раз. На begin request и end request создается и удаляется сессия для работы с NHibernate. Так же можно увидеть что для конфигурации используется файл hibernate.config. Добавим этот файл в корень прилоежния.

Содержимое этого файла:

<?xml version="1.0" encoding="utf-8"?>
<hibernate-configuration xmlns="urn:nhibernate-configuration-2.2">
<session-factory>
<property name="connection.connection_string">Data Source=10.10.5.101\sqlexpress;Database=nhibernate;UID=nhibernate;pwd=nhibernate;</property>
<property name="dialect">NHibernate.Dialect.MsSql2005Dialect</property>
<property name="connection.provider">NHibernate.Connection.DriverConnectionProvider</property>
<property name="connection.driver_class">NHibernate.Driver.SqlClientDriver</property>
<property name="proxyfactory.factory_class">NHibernate.ByteCode.LinFu.ProxyFactoryFactory, NHibernate.ByteCode.LinFu</property>
<mapping assembly="InheritanceCore" />
</session-factory>
</hibernate-configuration>

Настройки интуитивно понятные, кроме одной: proxyfactory.factory_class. Эта настройка добавлена начиная с Nhibernate версии 2.1.0, информацию о ней можно найти тут, для успешной работы нужно будет добавить ссылку на сборку NHibernate.ByteCode.LinFu, которая находится в папке Required_For_LazyLoading того же Nhibernate contrib.

Осталось последнее, рассказать Nhibernate’у какие классы есть у нас в приложении. Для этого есть несколько подходов, о них я напишу позже. В этом самом простом приложении воспользуемся наиболее старым но понятным подходом – xml файлы. Добавим в проект новый файл Product.hbm.xml, и поставим ему Build Action – Embedded Resource (иначе NHibernate не получит нужных ему маппингов). Содержимое этого файла:

<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="InheritanceCore"
namespace="InheritanceCore">
<class name="InheritanceCore.Product" table="Products" xmlns="urn:nhibernate-mapping-2.2">
<id name="Id" column="Id" type="Int32">
<generator class="native"/>
</id>
<property name="Name" column="Name" type="String"/>
<property name="Description" column="Description" type="String"/>
<property name="Price" column="Price" type="Double"/>
</class>
</hibernate-mapping>

Данный маппинг говорит о том, что есть класс, который соответствует таблице Products, содержит идентификатор с именем Id, значение которого генерируется при помощи базы данных (дополнительную информацию про id можно найти тут). И содержит 3 свойства, 2 текстовых, и 1 числовое.

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

clip_image004

Теперь выполним CRUD операции.

Для получения всех продуктов:

Global.CurrentSession.CreateCriteria(typeof(Product)).List<Product>()

Для создания продукта:

var product = new Product
{
Description = txtDesctiption.Text,
Name = txtName.Text,
Price = double.Parse(txtPrice.Text);
};
Global.CurrentSession.SaveOrUpdate(product);

Для обновления:

var product = Global.CurrentSession.Get<Product>(productId);
product.Description = "edited";
Global.CurrentSession.SaveOrUpdate(product);

Для удаления

var product = Global.CurrentSession.Get<Product>(productId);
Global.CurrentSession.Delete(product);

9 comments:

  1. Спасибо позновательно
    Продолжай :)

    ReplyDelete
  2. Да мне тоже понравилось.
    Буду следить за вашим блогом)

    ReplyDelete
  3. А за чем он нужен?*

    ReplyDelete
  4. как бы "использование в CRUD" - это самая суть NH. А написано мало.

    ReplyDelete
  5. CRUD это самая простая и скучная часть NH :)

    ReplyDelete
  6. "Набор ссылок на статьи по NHibernate для начинающих" - Там все ссылки не рабочие, у Ayendы

    ReplyDelete
  7. Оценивайте дату написания статьи ;) Со ссылками на еще более ранние очерки. Конечно, ссылки уже могут быть битыми.

    ReplyDelete
  8. Вот, вроде бы по делу набор статей, наконец-то. А то всё как то ограничивается базовым, маппингом и crud, и, конечно же, без связей

    ReplyDelete