Линки на остальные статьи
- Базовые понятия
- Варианты маппингов
- Связи
- Запросы при помощи Criteria
- Automapping
Любое бизнес приложение содержит набор некоторых объектов, комбинация который представляет собой доменную модель приложения. Например для электронного магазина такими доменными объектами могут быть: Product, Category, Order и т.д.
Для NHibernate необходимы знания о вашей доменной модели для сохранения и извлечения соответствующих объектов. Для этого используется подход называемый data mapper.
В этой статье будут рассмотрены доступные на данный момент варианты маппингов NHibernate. Будут рассмотрены:
- Xml файлы – базовый способ маппинга, все остальные способы сводятся к получению таких xml файлов.
- Аттрибуты – каждому полю или классу добавляются специальные аттрибуты, которые в дальшем преобразовываются в xml.
- Fluent маппинг – маппинг при помощи лямбда выражений.
Рассмотрим каждый из них подробнее.
1 Xml файлы
Базовый способ маппинга был показан в первой статье. Могу выделить лишь следущюее:
Преимущества:
- Самый первый из разработанных в NHibernate, соответственно в интернете множество можно найти примеров.
- Достаточно .net 2.0
Недостатки:
- Отсутствие валидации во время компиляции
- Невозможность переименовать поля при помощи Refactor (в вижуал студии есть отличная возможность для удобного переименовывания полей и методов. Для этого на нужном методе клацаем правой кнопкой, далее в меню выбираем Refactor->Rename. Эта опция переименует метод и обновит все вывозы этого метода или свойства в проекте)
- Отсутствеие полноценного intellisense (есть xsd схема для маппингов, но имена полей вашей сущности показывать все равно не будет)
2 Аттрибуты
Рассмотрим пример из первой части, только воспользуемся маппингами с помощью аттрибутов. Скачать нужную для этого библиотеку можно тут. После того, как добавлена ссылка на NHibernate.Mapping.Attributes класс Product (описан в первой части) должен выглядеть следующим образом:
[Class(Table = "Products", Name = "InheritanceCore.Product")] public class Product { [Id(0, Column = "Id", Type = "int", Name="Id")] [Generator(1, Class = "native")] public virtual int Id { get; protected set; } [Property(Name = "Name", Column = "Name", Type = "String")] public virtual string Name { get; set; } [Property(Name = "Description", Column = "Description", Type = "String")] public virtual string Description { get; set; } [Property(Name = "Price", Column = "Price", Type = "Double")] public virtual double Price { get; set; } }
Также надо изменить Global.asax следующим образом:
protected static ISessionFactory CreateSessionFactory() { var config = new Configuration().Configure(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "hibernate.config")); HbmSerializer.Default.Validate = true; var assembly = Assembly.GetAssembly(typeof (Product)); HbmSerializer.Default.HbmAssembly = assembly.FullName; HbmSerializer.Default.HbmNamespace = typeof(Product).Namespace; var stream = HbmSerializer.Default.Serialize(assembly); config.AddInputStream(stream); return config.BuildSessionFactory(); }
Вот и все, остальное остается с первой части без изменений. Таким образом:
Преимущества:
- Сущности и маппинги находятся рядом
- Наличие некоторого intellisense
Недостатки
- Наличие строковых литералов. (Свойства Name, Type). Из-за этого невозможно использовать Refactor и полноценно переименовывать свойства классов
- Отсутствие валидации во время компиляции. На самом деле можно все атрибуты повесить на одно свойство, результат будет такой же
- Доменные объекты засоряются огромным количеством аттрибутов, которые усложняют читабельность кода
3 Fluent маппинг
Это наиболее новый способ маппинга.
Рассмотрим как с его помощью замапить тот же самый класс Product.
В этом случае надо изменить метод в global.asax следующим образом:
protected static ISessionFactory CreateSessionFactory() { return Fluently.Configure() .Database(MsSqlConfiguration.MsSql2005.ConnectionString(@"your connection string")) .Mappings(m => m.FluentMappings.AddFromAssemblyOf<product>()) .BuildSessionFactory(); }
Для маппинга в проект добавляется еще один класс. Как правило его называют <ClassName>Map, т.е. у для примера Product это будет класс ProductMap. Его содержимое:
public class ProductMap : ClassMap<Product> { public ProductMap() { Table("Products"); Id(x => x.Id).GeneratedBy.Native(); Map(x => x.Description).Column("Description"); Map(x => x.Name).Column("Name"); Map(x => x.Price).Column("Price"); } }
Тут мы имеем полный intellisense, и все прелести проверки время компиляции.
Немного сумбурно, новичкам могут быть непонятны многие термины, например "доменные объекты", "refactor". Больше похоже на введение в NHibernate для опытного разработчика =)
ReplyDeleteНадо будет поработать над этим...
ReplyDeleteА мне понравилось. Продолжай Sly. Интересно понятно актуально.
ReplyDeleteЯ мало что понял. Винигрет какойто.
ReplyDeleteА пытался понять? Очень ценный комментарий :)
ReplyDeleteотлично. Наконец то появилось хоть немного нормальной инфы на русском по хибернейту ))
ReplyDeleteПоясните новичку где взять:
ReplyDelete"Path". В этой строке среда ругается что переменная
"Path" не объявлена:
"...Configure(Path.Combine(AppDomain.Cur..."
System.IO.Path
ReplyDeleteСпасибо!
ReplyDeleteнасчет типов в маппинге аттрибутами.. можно определить расширяющий метод, который вернет имя типа в строке, тоесть GetType() разбить по разделителю '.' и вернуть последний элемент массива.
ReplyDeleteПочему-то пример с атрибутами не заработал(( The element 'class' in namespace 'urn:nhibernate-mapping-2.2' has invalid child element 'property' in namespace 'urn:nhibernate-mapping-2.2'
ReplyDeleteПравильно ли я понял - Refactor-Rename не будет работать ни в одном из трех способов, включая Fluent маппинг?
ReplyDeleteНе могу найти причину.
ReplyDeleteРеализую следующее:
return Fluently.Configure()
.Database(MySQLConfiguration.Standard.ConnectionString(
cs => cs.Server("78....")
.Database("s...")
.Username("co...")
.Password("Nu...")))
.Mappings(m => m.FluentMappings.AddFromAssemblyOf())
.BuildSessionFactory();
без строки " .Mappings(m => m.FluentMappings.AddFromAssemblyOf())" все работает
но с мапингом выдает следующую ошибку:
"An invalid or incomplete configuration was used while creating a SessionFactory. Check PotentialReasons collection, and InnerException for more detail."
подскажите пожалуйста
This comment has been removed by the author.
DeleteOf() = Of{Product}() - скрылись при публикации "острые" скобки
Delete