NHibernate - Краткое руководство

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

Что такое NHibernate?

NHibernate - это зрелый объектно-реляционный преобразователь с открытым исходным кодом для платформы .NET. Он активно развивается, полно функционал и используется в тысячах успешных проектов. Он построен поверхADO.NET и текущая версия NHibernate 4.0.4.

  • NHibernate - это объектно-реляционный преобразователь .NET с открытым исходным кодом, который распространяется под GNU Lesser General Public License.

  • Он основан на Hibernate, популярном объектно-реляционном картографе Java, и имеет очень зрелую и активную кодовую базу.

  • Он обеспечивает основу для сопоставления объектно-ориентированной модели предметной области с традиционной реляционной базой данных.

  • NHibernate был запущен Tom Barrett и этот проект существует с февраля 2003 года, когда они впервые совершили коммит.

  • Это большой проект, предлагающий множество функций.

  • Существует NuGet package доступно, что упрощает добавление в проект.

Почему NHibernate?

Теперь вопрос, зачем нам object-relational mappers? Это потому, что существует разрыв между объектным миром и миром отношений.

  • В объектном мире все основано на objects; мы назвали объектами те вещи, которые имеют наши данные.

  • Реляционный мир основан на множествах, и мы имеем дело с таблицами и строками, которые отличаются от объектного мира.

  • В объектном мире у нас есть unidirectional associations. Если у покупателя есть указатель на заказ, это не обязательно означает, что в заказе есть указатель на покупателя, это может быть, а может и нет.

  • В мире отношений все ассоциации bidirectional и это можно сделать с помощью внешнего ключа.

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

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

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

  • В мире отношений нет такого понятия наследования.

Картография

Итак, как нам сопоставить все это disjoint relationships?Эта концепция сопоставления исходит от объектно-реляционного сопоставителя. В основном необходимо понимать три вещи, как показано на следующей диаграмме.

  • В вашем приложении вам потребуются определения классов, которые обычно представляют собой код C # и его код .NET, представляющий наши классы, такие как класс Employee, класс Customer, класс заказа и т. Д.

  • Внизу вы можете увидеть схему базы данных, которая является нашим Data Definition Language в реляционной базе данных, которая определяет, как выглядит таблица клиентов, как выглядит таблица сотрудников.

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

  • Эти метаданные сопоставления могут быть представлены множеством различных способов, и мы рассмотрим ряд этих различных способов, наиболее типичных для приложения NHibernate.

  • Он представлен HBM (Hibernate Mapping) файлы, которые являются файлами XML.

База данных поддерживается

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

  • SQL-сервер является основной поддерживаемой базой данных, это то, что большинство разработчиков используют во время разработки, вероятно, наиболее распространенная.

  • Это также works very well with Oracle.

  • Он также поддерживает DB2, Firebird, MySQL, PostgreSQL, SQL Lite.

  • Он также имеет ODBC and OLEDB drivers.

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

Многослойная архитектура

Многоуровневая архитектура делит систему на несколько групп, каждая из которых содержит код, обращающийся к определенной проблемной области, и эти группы называются уровнями. Большинство приложений корпоративного уровня используютhigh-level application architecture которые состоят из трех слоев -

  • Слой презентации
  • Уровень бизнеса
  • Слой постоянства

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

Одним из основных преимуществ многоуровневого подхода является то, что вы часто можете вносить изменения в один уровень без какого-либо значительного нарушения работы других слоев, что делает системы lesser fragile and more maintainable.

Слой презентации

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

Бизнес-уровень

  • Бизнес-уровень отвечает за реализацию любых бизнес-правил или системных требований, которые пользователи понимают как часть проблемной области.

  • Он также повторно использует модель, определенную слоем постоянства.

Слой стойкости

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

  • Этот уровень также определяет соответствие между классом модели и базой данных. NHibernate используется в основном в этом слое.

База данных

  • База данных существует вне приложения .NET.
  • Это фактическое постоянное представление состояния системы.
  • Если используется база данных SQL, она включает реляционную схему и, возможно, хранимые процедуры.

Вспомогательные / служебные классы

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

  • Эти элементы не считаются слоями, потому что они не подчиняются правилам межуровневой зависимости в многоуровневой архитектуре.

Архитектура NHibernate

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

  • Код приложения использует NHibernate. ISession и IQuery API-интерфейсы для операций с постоянством и только для управления транзакциями базы данных, в идеале с использованием NHibernate ITransaction API.

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

Что такое ORM?

Объектно-реляционное отображение (ORM) - это programming techniqueдля преобразования данных между несовместимыми системами типов в объектно-ориентированных языках программирования. Другими словами, это концепция отображения бизнес-объектов приложения на таблицы реляционной базы данных, чтобы можно было легко получить доступ к данным и полностью обновить их через объектную модель приложения.

  • Как вы уже знаете, реляционные базы данных предоставляют хорошие средства для хранения данных, а объектно-ориентированное программирование - хороший подход к созданию сложных приложений.

  • NHibernate и ORM в целом наиболее подходят для приложений с нетривиальной бизнес-логикой, моделью предметной области и своего рода базой данных.

  • С ORM очень легко создать уровень перевода, который может легко преобразовывать объекты в реляционные данные и обратно.

  • Аббревиатура ORM может также означать моделирование ролей объекта, и этот термин был изобретен до того, как стало актуальным отображение объектов / отношений.

  • Он описывает метод анализа информации, используемый при моделировании баз данных.

Почему ORM?

ORM - это framework который позволяет отображать мир объектов, найденных в объектно-ориентированных языках, на строки в реляционных таблицах, найденных в реляционных базах данных

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

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

  • У нас есть столбец для идентификатора, который однозначно идентифицирует каждого сотрудника.

  • Столбец для имени сотрудника, еще один столбец для его даты присоединения и, наконец, столбец, в котором указан возраст сотрудника.

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

  • На приведенной выше диаграмме вы также можете видеть, что у нас есть объект сотрудника, у которого есть поля для идентификатора, имени, даты присоединения и возраста.

  • Без ORM мы должны преобразовать этот объект в несколько различных операторов SQL, которые будут вставлять данные о сотрудниках в таблицу сотрудников.

  • Так что написать код для создания SQL для выполнения описанного выше сценария не так уж сложно, но это немного утомительно и довольно легко ошибиться.

  • Используя ORM, например NHibernate, мы можем объявить, как определенные классы должны быть сопоставлены с реляционными таблицами, и позволить ORM или NHibernate справиться с неприятной работой по созданию SQL для вставки, обновления, удаления в данных запроса в нашей таблице сотрудников.

  • Это позволяет нам сосредоточить наш код на использовании объектов и автоматически преобразовывать эти объекты в реляционные таблицы.

  • На самом деле ORM избавляет нас от необходимости вручную отображать объекты в таблицы.

Чтобы начать работу над NHibernate, нам потребуются Visual Studio и пакет NHibernate.

Установка Visual Studio

Microsoft предоставляет free version Visual Studio, который также содержит SQL Server и его можно скачать с https://www.visualstudio.com Ниже приведены шаги по установке.

Step 1 - После завершения загрузки запустите программу установки, после чего отобразится следующее диалоговое окно.

Step 2 - Нажмите кнопку «Установить», и начнется процесс установки.

Step 3 - После успешного завершения процесса установки вы увидите следующее диалоговое окно.

Step 4 - Закройте это диалоговое окно и при необходимости перезагрузите компьютер.

Step 5- Теперь откройте Visual Studio из меню «Пуск», в котором откроется следующий диалог. Первое время на подготовку уйдет какое-то время.

Step 6 - Как только все это будет сделано, вы увидите главное окно Visual Studio.

Установка пакета NHibernate

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

Прямая загрузка

  • Загрузите zip из файла из https://sourceforge.net/ который содержит все необходимые двоичные файлы.

  • Распакуйте этот zip-файл и включите все эти двоичные файлы в свой проект.

Установить с помощью NuGet

  • Другой способ установки NHibernate - использовать NuGet для установки пакета NHibernate, что, безусловно, является самым простым способом включения NHibernate в проект.

  • Он загрузит все зависимости NHibernate и создаст ссылки на все необходимые сборки.

  • Чтобы установить NHibernate, выполните следующую команду в консоли диспетчера пакетов.

install-package NHibernate

Теперь вы готовы начать свое приложение.

В этой главе мы рассмотрим, как запустить простой пример с помощью NHibernate. Мы будем строитьsimple console application. Для создания консольного приложения мы будем использовать Visual Studio 2015, который содержит все функции, которые вам нужно создать, протестируйте ваше приложение с помощью пакета NHibernate.

Ниже приведены шаги по созданию проекта с использованием шаблонов проектов, доступных в Visual Studio.

Step 1 - Откройте Visual Studio и выберите пункт меню «Файл» → «Создать» → «Проект».

Step 2 - Откроется диалоговое окно нового проекта.

Step 3 - На левой панели выберите «Шаблоны» → «Visual C #» → «Windows».

Step 4 - На средней панели выберите Консольное приложение.

Step 5 - Введите имя проекта «NHibernateDemoApp» в поле «Имя» и нажмите «ОК», чтобы продолжить.

Step 6 - После того как проект будет создан Visual Studio, вы увидите ряд файлов, отображаемых в окне обозревателя решений.

Как вы знаете, мы создали проект простого консольного приложения, теперь нам нужно включить пакет NHibernate в наш консольный проект.

Перейдите в меню Сервис и выберите Диспетчер пакетов NuGet → Консоль диспетчера пакетов, откроется окно консоли диспетчера пакетов.

Укажите команду, показанную выше Package Manager Consoleокна и нажмите Enter, он загрузит все зависимости NHibernate и создаст ссылки на все необходимые сборки. После завершения установки вы увидите сообщение, как показано на следующем изображении.

Теперь, когда у нас добавлен NHibernate, мы можем приступить к реализации. Итак, мы собираемся начать с сопоставления очень простогоtable называется Student, который просто имеет целочисленный первичный ключ с именем ID и столбцы FirstName и LastName.

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

Войти Student.csв поле имени нажмите кнопку Добавить. В этом классе Student нам нужен целочисленный первичный ключ с именем ID, и нам нужно создать эту строку,FirstName и LastName поля, как показано в следующей полной реализации класса Student.

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.Threading.Tasks;

namespace NHibernateDemoApp { 
   
   class Student { 
      public virtual int ID { get; set; } 
      public virtual string LastName { get; set; } 
      public virtual string FirstMidName { get; set; } 
   } 
}

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

Теперь перейдем к методу Main в классе Program и создадим новый объект конфигурации NHibernate.

Первое, что нам нужно предоставить, это connection string. Это строка подключения для конкретной базы данных, и самый простой способ найти строку подключения - щелкнуть правой кнопкой мыши базу данных вSQL Server Object Explorer и выберите Свойства.

Откроется окно свойств. Прокрутите вниз, и вы увидите поле строки подключения в окне свойств.

Скопируйте строку подключения и укажите в своем коде. Ниже приведена реализация метода Main, в котором нам нужна конфигурация для NHibernate.

using NHibernate.Cfg;
using NHibernate.Dialect;
using NHibernate.Driver;

using System;
using System.Linq;
using System.Reflection;

namespace NHibernateDemoApp {

   class Program {
   
      static void Main(string[] args) {
         var cfg = new Configuration();
			
         String Data Source = asia13797\\sqlexpress;
         String Initial Catalog = NHibernateDemoDB;
         String Integrated Security = True;
         String Connect Timeout = 15;
         String Encrypt = False;
         String TrustServerCertificate = False;
         String ApplicationIntent = ReadWrite;
         String MultiSubnetFailover = False;
			
         cfg.DataBaseIntegration(x = > { x.ConnectionString = "Data Source + 
            Initial Catalog + Integrated Security + Connect Timeout + Encrypt +
            TrustServerCertificate + ApplicationIntent + MultiSubnetFailover";
         
                     
            x.Driver<SqlClientDriver>(); 
            x.Dialect<MsSql2008Dialect>();
         });
         
         cfg.AddAssembly(Assembly.GetExecutingAssembly());
         
         var sefact = cfg.BuildSessionFactory(); 
         
         using (var session = sefact.OpenSession()) {
         
            using (var tx = session.BeginTransaction()) {
               //perform database logic 
               tx.Commit();
            }
            
            Console.ReadLine(); 
         } 
      } 
   } 
}

После строки подключения нам нужно указать драйвер, который является SQLClientDriver а затем нам также нужно указать диалект, какая версия SQL Server, и мы собираемся использовать MS SQL 2008.

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

Мы можем сделать это, добавив сборку, поэтому, указав Assembly.GetExecutingAssemblyи здесь программа найдет файлы сопоставления. Файлы сопоставления сообщают NHibernate, как перейти от классов C # к таблицам базы данных.

SessionFactory компилирует все метаданные, необходимые для инициализации NHibernate. SessionFactory можно использовать для создания сеансов, которые примерно аналогичны соединениям с базой данных. Таким образом, подходящий способ - использовать его в блоке using. я могу сказатьvar session равно sessionFactory.OpenSession и я хочу сделать это внутри его транзакции.

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

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

Итак, давайте продолжим и добавим новый XML-файл, щелкнув правой кнопкой мыши проект в проводнике решений и выбрав Добавить → Новый элемент ...

Войти Student.hbm.xmlв поле имени. Нам нужно указать сборку по умолчанию, которая будетNHibernateDemoAppа также укажите пространство имен по умолчанию. Это просто сокращает многие другие определения типов, которые мы собираемся сделать в этом файле.

Ниже приведена реализация в файле XML -

<?xml version = "1.0" encoding = "utf-8" ?> 

<hibernate-mapping xmlns = "urn:nhibernate-mapping-2.2" 
   assembly = "NHibernateDemoApp" namespace = "NHibernateDemoApp">

   <class name = "Student"> 
      <id name = "ID">
        <generator class = "native"/> 
      </id> 
		
      <property name = "LastName"/> 
      <property name = "FirstMidName"/> 
   </class> 
   
</hibernate-mapping>

Следующее, что нам нужно, это определить класс; этот класс будет нашимStudent class. Затем нам нужно сообщить NHibernate имя идентификатора, то есть ID, и я также должен сообщить NHibernate, как сгенерировать идентификаторы, поэтому наш генератор будет иметь тип native.

Генератор собственных типов означает, что в такой базе данных, как SQL Server, он будет использовать столбец идентификаторов, тип идентификаторов.

Следующее, что нам нужно сделать, это дать имена свойствам. Итак, добавьте еще два свойства для FirstName и LastName.

Теперь мы читаем эти файлы сопоставления из сборки. Поэтому предпочтительный способ сделать это - иметь этиHBM filesвстроен в вашу сборку. Мы можем сделать это, просто установив свойство.

Теперь щелкните правой кнопкой мыши проект в проводнике решений и выберите Свойства, вы увидите Build Action field в котором Контент выбран по умолчанию.

Выберите встроенный ресурс из раскрывающегося списка.

Таким образом, этот XML-файл фактически вставляется в NHibernateDemoApp сборка.

В этой главе мы рассмотрим основные CRUD operations. Теперь, когда наша система готова к запуску, так как мы успешно реализовали наш предметный класс Student, мы также определили файлы сопоставления и настроили NHibernate. Теперь мы можем использовать некоторые запросы для выполнения операций CRUD.

Создать данные

Как видите, у нас нет данных в нашей таблице Student в NHibernateDemoDB база данных.

Итак, чтобы добавить данные, нам нужно выполнить Add/Create как показано ниже.

using (var session = sefact.OpenSession()) { 

   using (var tx = session.BeginTransaction()) { 
     
      var student1 = new Student { 
         ID = 1, 
         FirstMidName = "Allan", 
         LastName = "Bommer" 
      }; 
      
      var student2 = new Student { 
         ID = 2, 
         FirstMidName = "Jerry", 
         LastName = "Lewis" 
      }; 
      
      session.Save(student1); 
      session.Save(student2); 
      tx.Commit(); 
   } 
   
   Console.ReadLine(); 
}

Как видите, мы создали двух студентов, а затем вызываем метод Save () объекта OpenSession а затем вызовите Commit () BeginTransaction. Вот полная реализация вProgram.cs файл

using NHibernate.Cfg; 
using NHibernate.Dialect; 
using NHibernate.Driver; 

using System; 
using System.Linq; 
using System.Reflection;

namespace NHibernateDemoApp { 
   
   class Program { 
      
      static void Main(string[] args) { 
         var cfg = new Configuration();
			
         String Data Source = asia13797\\sqlexpress;
         String Initial Catalog = NHibernateDemoDB;
         String Integrated Security = True;
         String Connect Timeout = 15;
         String Encrypt = False;
         String TrustServerCertificate = False;
         String ApplicationIntent = ReadWrite;
         String MultiSubnetFailover = False;
			
         cfg.DataBaseIntegration(x = > { x.ConnectionString = "Data Source + 
            Initial Catalog + Integrated Security + Connect Timeout + Encrypt +
            TrustServerCertificate + ApplicationIntent + MultiSubnetFailover"; 

            x.Driver<SqlClientDriver>(); 
            x.Dialect<MsSql2008Dialect>(); 
         }); 
   
         cfg.AddAssembly(Assembly.GetExecutingAssembly()); 
         var sefact = cfg.BuildSessionFactory(); 
			
         using (var session = sefact.OpenSession()) { 
			
            using (var tx = session.BeginTransaction()) { 
               
               var student1 = new Student { 
                  ID = 1,  
                  FirstMidName = "Allan", 
                  LastName = "Bommer" 
               }; 

               var student2 = new Student { 
                  ID = 2, 
                  FirstMidName = "Jerry", 
                  LastName = "Lewis" 
               }; 
            
               session.Save(student1); 
               session.Save(student2); 
               tx.Commit();
            } 
            
            Console.ReadLine(); 
         } 
      } 
   } 
}

Теперь давайте запустим это приложение, а затем перейдем в обозреватель объектов SQL Server и обновим вашу базу данных. Вы увидите, что эти два студента теперь добавлены в таблицу Student в базе данных NHibernateDemoDB.

Чтение данных из таблицы учеников

Вы можете видеть, что теперь у нас есть две записи в нашей таблице студентов. Чтобы прочитать эти записи из таблицы, нам нужно вызватьCreateCriteria() OpenSession, как показано в следующем коде.

using (var session = sefact.OpenSession()) { 
   
   using (var tx = session.BeginTransaction()) { 
      var students = session.CreateCriteria<Student>().List<Student>(); 
      
      foreach (var student in students) { 
         Console.WriteLine("{0} \t{1} \t{2}", 
            student.ID,student.FirstMidName, student.LastName); 
      } 
      
      tx.Commit(); 
   } 
   
   Console.ReadLine(); 
}

Итак, если вам нужен список записей, мы можем просто сказать список типа Student.

Теперь используйте foreach через всех студентов и скажите распечатайте удостоверение личности, FirstMidName и LastNameна консоли. Теперь давайте снова запустим это приложение, и вы увидите следующий вывод в окне консоли.

1 Allan Bommer
2 Jerry Lewis

Вы также можете получить любую запись, указав идентификатор в Get() метод OpenSession, используя следующий код.

using (var session = sefact.OpenSession()) { 
   
   using (var tx = session.BeginTransaction()) { 
      var students = session.CreateCriteria<Student>().List<Student>(); 
      
      foreach (var student in students) { 
         Console.WriteLine("{0} \t{1} \t{2}", student.ID, 
            student.FirstMidName, student.LastName); 
      }
      
      var stdnt = session.Get<Student>(1); 
      Console.WriteLine("Retrieved by ID"); 
      Console.WriteLine("{0} \t{1} \t{2}", stdnt.ID, 
         stdnt.FirstMidName, stdnt.LastName); 
      tx.Commit();
   } 
	
   Console.ReadLine(); 
}

Теперь, когда вы запустите свое приложение, вы увидите следующий результат.

1 Allan Bommer
2 Jerry Lewis
Retrieved by ID
1 Allan Bommer

Обновить запись

Чтобы обновить запись в таблице, нам нужно сначала получить эту конкретную запись, а затем обновить эту запись, вызвав Update() метод OpenSession, как показано в следующем коде.

using (var session = sefact.OpenSession()) { 

   using (var tx = session.BeginTransaction()) { 
      var students = session.CreateCriteria<Student>().List<Student>(); 
     
      foreach (var student in students) { 
         Console.WriteLine("{0} \t{1} \t{2}", student.ID, 
            student.FirstMidName, student.LastName); 
      }
      
      var stdnt = session.Get<Student>(1); 
      Console.WriteLine("Retrieved by ID"); 
      Console.WriteLine("{0} \t{1} \t{2}", stdnt.ID, stdnt.FirstMidName, stdnt.LastName);
      
      Console.WriteLine("Update the last name of ID = {0}", stdnt.ID); 
      stdnt.LastName = "Donald"; 
      session.Update(stdnt); 
      Console.WriteLine("\nFetch the complete list again\n"); 
      
      foreach (var student in students) { 
         Console.WriteLine("{0} \t{1} \t{2}", student.ID, 
            student.FirstMidName, student.LastName); 
      } 
      
      tx.Commit();
   } 
   
   Console.ReadLine();
}

Теперь, когда вы запустите свое приложение, вы увидите следующий результат.

1 Allan Bommer
2 Jerry Lewis
Retrieved by ID
1 Allan Bommer
Update the last name of ID = 1
Fetch the complete list again
1 Allan Donald
2 Jerry Lewis

Как видите, LastName с идентификатором, равным 1, обновляется с Bommer на Donald.

Удалить запись

Чтобы удалить любую запись из таблицы, нам нужно сначала получить эту конкретную запись, а затем удалить эту запись, вызвав Delete() метод OpenSession, как показано в следующем коде.

using (var session = sefact.OpenSession()) { 
   
   using (var tx = session.BeginTransaction()) { 
      var students = session.CreateCriteria<Student>().List<Student>();
      
      foreach (var student in students) { 
         Console.WriteLine("{0} \t{1} \t{2}", student.ID, 
            student.FirstMidName, student.LastName); 
      }
      
      var stdnt = session.Get<Student>(1); 
      Console.WriteLine("Retrieved by ID"); 
      Console.WriteLine("{0} \t{1} \t{2}", stdnt.ID, stdnt.FirstMidName, stdnt.LastName);
      
      Console.WriteLine("Delete the record which has ID = {0}", stdnt.ID); 
      session.Delete(stdnt);
      Console.WriteLine("\nFetch the complete list again\n"); 
      
      foreach (var student in students) { 
         Console.WriteLine("{0} \t{1} \t{2}", student.ID, student.FirstMidName, 
            student.LastName); 
      } 
      
      tx.Commit();
   } 
	
   Console.ReadLine(); 
}

Теперь, когда вы запустите свое приложение, вы увидите следующий результат.

1 Allan Donald
2 Jerry Lewis
Retrieved by ID
1 Allan Bommer
Delete the record which has ID = 1
Fetch the complete list again
2 Jerry Lewis

Как видите, запись с идентификатором 1 больше не доступна в базе данных. Вы также можете увидеть базу данных в обозревателе объектов SQL Server.

В этой главе мы поймем, как все записи из базы данных retrieved, updated, created, and deleted и как именно эти запросы выполняются?

Чтобы понять все это, мы можем просто добавить в нашу конфигурацию параметр, который регистрирует SQL в консоли. Вот простой оператор, который будет регистрировать SQL-запрос:

x.LogSqlInConsole = true;

Теперь у нас есть две записи в нашей таблице студентов в базе данных NHibernateDemoDB. Получим все записи из базы данных, как показано в следующем коде.

using NHibernate.Cfg; 
using NHibernate.Dialect; 
using NHibernate.Driver; 

using System; 
using System.Linq; 
using System.Reflection;

namespace NHibernateDemoApp { 

   class Program { 
      
      static void Main(string[] args) { 
         var cfg = new Configuration();
			
         String Data Source = asia13797\\sqlexpress;
         String Initial Catalog = NHibernateDemoDB;
         String Integrated Security = True;
         String Connect Timeout = 15;
         String Encrypt = False;
         String TrustServerCertificate = False;
         String ApplicationIntent = ReadWrite;
         String MultiSubnetFailover = False;			
         
         cfg.DataBaseIntegration(x = > { x.ConnectionString = "Data Source + 
            Initial Catalog + Integrated Security + Connect Timeout + Encrypt +
            TrustServerCertificate + ApplicationIntent + MultiSubnetFailover"; 
            
            x.Driver<SqlClientDriver>(); 
            x.Dialect<MsSql2008Dialect>(); 
            x.LogSqlInConsole = true; 
         }); 
      
         cfg.AddAssembly(Assembly.GetExecutingAssembly()); 
         var sefact = cfg.BuildSessionFactory();
      
         using (var session = sefact.OpenSession()) { 
         
            using (var tx = session.BeginTransaction()) { 
               Console.WriteLine("\nFetch the complete list again\n");
               var students = session.CreateCriteria<Student>().List<Student>(); 
      
               foreach (var student in students) { 
                  Console.WriteLine("{0} \t{1} \t{2}", student.ID, student.FirstMidName,
                     student.LastName); 
               } 
               
               tx.Commit(); 
            } 
				
            Console.ReadLine(); 
         } 
      } 
   } 
}

Итак, давайте продолжим и снова запустим это приложение, и вы увидите следующий результат -

NHibernate: SELECT this_.ID as ID0_0_, this_.LastName as LastName0_0_,
   this_.FirstMidName as FirstMid3_0_0_ FROM Student this_

Fetch the complete list again

3 Allan Bommer
4 Jerry Lewis

Как видите, select clauseбудучи отправленным в базу данных, это фактически похоже на предложение, которое извлекает ID, FirstMidName и LastName. Таким образом, все это отправляется в базу данных и обрабатывается там, вместо того, чтобы иметь много записей, возвращаемых на ваш сервер и обрабатываемых на стороне сервера.

Профилировщик NHibernate

Другой способ взглянуть на эти результаты - использовать NHibernate Profiler. NHibernate Profiler - это коммерческий инструмент, но он очень полезен для работы с приложениями NHibernate. Вы можете легко установить NHibernate Profiler в свое приложение из NuGet.

Перейдем в консоль NuGet Manager из меню Инструменты, выбрав Диспетчер пакетов NuGet → Консоль диспетчера пакетов. Откроется окно консоли диспетчера пакетов. Введите следующую команду и нажмите ввод.

PM> install-package NHibernateProfiler

Он установит все необходимые двоичные файлы для NHibernate Profiler, после его успешной установки вы увидите следующее сообщение.

Вы также увидите, что NHibernate Profiler запускается после его установки. Для его использования потребуется лицензия, но для демонстрационных целей мы можем использовать 30-дневную пробную версию NHibernate Profiler.

Теперь NHibernate Profiler оптимизирован для работы с веб-приложениями, и вы увидите, что он добавил App_Start folderв обозревателе решений. Чтобы все было просто, удалите папку App_Start, и вы также увидите, что один оператор добавлен в начало метода Main в классе Program.

App_Start.NHibernateProfilerBootstrapper.PreStart();

Удалите также этот оператор и просто добавьте простой вызов NHibernateProfiler.Initialize как показано в следующем коде.

using HibernatingRhinos.Profiler.Appender.NHibernate; 
using NHibernate.Cfg; 
using NHibernate.Dialect; 
using NHibernate.Driver; 

using System; 
using System.Linq; 
using System.Reflection;

namespace NHibernateDemoApp { 
   
   class Program { 
	
      static void Main(string[] args) { 
		
         NHibernateProfiler.Initialize(); 
         var cfg = new Configuration();
			
         String Data Source = asia13797\\sqlexpress;
         String Initial Catalog = NHibernateDemoDB;
         String Integrated Security = True;
         String Connect Timeout = 15;
         String Encrypt = False;
         String TrustServerCertificate = False;
         String ApplicationIntent = ReadWrite;
         String MultiSubnetFailover = False;			
         
         cfg.DataBaseIntegration(x = > { x.ConnectionString = "Data Source + 
            Initial Catalog + Integrated Security + Connect Timeout + Encrypt +
            TrustServerCertificate + ApplicationIntent + MultiSubnetFailover";
				
            x.Driver<SqlClientDriver>(); 
            x.Dialect<MsSql2008Dialect>(); 
            x.LogSqlInConsole = true; 
         }); 

         cfg.AddAssembly(Assembly.GetExecutingAssembly()); 
         var sefact = cfg.BuildSessionFactory(); 
         
         using (var session = sefact.OpenSession()) { 
            
            using (var tx = session.BeginTransaction()){ 
               var students = session.CreateCriteria<Student>().List<Student>(); 
               Console.WriteLine("\nFetch the complete list again\n");
               
               foreach (var student in students) { 
                  Console.WriteLine("{0} \t{1} \t{2}", student.ID, student.FirstMidName,
                     student.LastName); 
               } 
					
               tx.Commit(); 
            } 
				
            Console.ReadLine(); 
         } 
      } 
   
   }
}

Теперь, когда вы запустите приложение, оно отправит данные в приложение NHibernate Profiler.

Вы можете видеть здесь, у нас есть красивый дисплей, который показывает, что мы начали транзакцию, что SQL делает с базой данных в удобном формате.

Так что это очень полезно для определения того, что именно происходит внутри вашего приложения NHibernate. Это становится невероятно полезным, когда приложение достигает определенного уровня сложности, когда вам нужно нечто большее, чем профилировщик SQL, но со знанием NHibernate.

В этой главе мы добавим IntelliSense в наши файлы сопоставления NHibernate (*.hbm.xml files). Как вы заметили при сопоставлении класса Student домена, в настоящее время у нас нет доступной IntelliSense. Очень полезно иметьXML schemasимеется в наличии. Итак, в этой главе вы поймете, как добавить IntelliSense в Visual Studio для этих XML-файлов NHibernate.

Откройте файл сопоставления, и вы увидите, что пункт меню XML появляется в главном меню.

Выберите пункт меню XML → Schemas…, и откроется диалоговое окно XML Schemas.

Нажмите кнопку Добавить…, которая находится в правом верхнем углу диалогового окна, которое открывает диалоговое окно файла. Теперь перейдите кpackages folder, который находится в папке решения вашего проекта, и вы увидите различные пакеты, включенные в ваш проект.

Теперь дважды щелкните NHibernate.4.*** folder и вы увидите два файла схем (* .xsd) или файлы определения схемы XML, которые определяют конфигурацию и отображение NHibernate.

Выберите эти два файла схемы и нажмите кнопку «Открыть».

Вы можете видеть, что схемы NHibernate добавляются в диалоговое окно XML Schemas. Щелкните кнопку ОК. Теперь давайте начнем новый тег свойства, и вы увидите, что здесь у нас есть полная версия IntelliSense.

Теперь вам доступен IntelliSense, который экономит много времени при объектно-реляционном сопоставлении.

В этой главе мы рассмотрим отображение типов данных. Сопоставление сущностей простое, классы сущностей всегда сопоставляются с таблицами базы данных с помощью<class>, <subclass>, and <joined-subclass>отображение элементов. Типам значений нужно нечто большее, и именно здесь требуются типы сопоставления.

NHibernate может отображать самые разные типы данных. Вот список наиболее распространенных поддерживаемых типов данных.

Тип отображения Тип .NET System.Data.DbType
Int16 System.Int16 DbType.Int16
Int32 System.Int32 DbType.Int32
Int64 System.Int64 DbType.Int64
не замужем System.Single DbType.Single
Двойной System.Double DbType.Double
Десятичный System.Decimal DbType.Decimal
Строка System.String DbType.String
AnsiString System.String DbType.AnsiString
Байт System.Byte DbType.Byte
Char System.Char DbType.StringFixedLength - один символ
AnsiChar System.Char DbType.AnsiStringFixedLength - один символ
Булево System.Boolean DbType.Boolean
Гид System.Guid DbType.Guid
PersistentEnum System.Enum (перечисление) DbType для базового значения
TrueFalse System.Boolean DbType.AnsiStringFixedLength - либо «T», либо «F»
Да нет System.Boolean DbType.AnsiStringFixedLength - «Y» или «N»
DateTime DateTime DbType.DateTime - миллисекунды игнорируются.
Клещи System.DateTime DbType.Int64
Промежуток времени System.TimeSpan DbType.Int64
Отметка времени System.DateTime DbType.DateTime - настолько конкретный, насколько поддерживает база данных.
Двоичный System.Byte [] DbType.Binary
BinaryBlob System.Byte [] DbType.Binary
StringClob System.String DbType.String
Сериализуемый Любой System.Object, помеченный SerializableAttribute DbType.Binary
CultureInfo System.Globalization.CultureInfo DbType.String - пять символов для языка и региональных параметров.
Тип Тип системы DbType.String, содержащая полное имя сборки

В приведенной выше таблице подробно объясняются указанные ниже указатели.

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

  • Он также может отображать Booleans, как в поля, использующие нули и единицы, символьные поля, содержащие true, false или T и F.

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

  • Мы можем справиться с отображением DateTime, включая и исключая смещения часовых поясов, летнее время и т. д.

  • Мы также можем отобразить enumerations; мы можем сопоставить их со строками или соответствующими числовыми значениями.

Давайте рассмотрим простой пример, в котором у нас есть одинаковые имена свойств как в базе данных, так и в классе Student.

Теперь давайте изменим FirstMidName на FirstName в классе ученика, где мы не будем изменять столбец FirstMidName, но посмотрим, как сообщить NHibernate, что нужно выполнить это преобразование. Ниже приведен обновленный класс учащихся.

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.Threading.Tasks;

namespace NHibernateDemoApp { 
  
   class Student { 
      public virtual int ID { get; set; } 
      public virtual string LastName { get; set; } 
      public virtual string FirstName { get; set; } 
   }
}

Вот реализация файла сопоставления NHibernate.

<?xml version = "1.0" encoding = "utf-8" ?> 
<hibernate-mapping xmlns = "urn:nhibernate-mapping-2.2" assembly = "NHibernateDemoApp" 
   namespace = "NHibernateDemoApp"> 
   
   <class name = "Student">
	
      <id name = "ID"> 
         <generator class = "native"/>
      </id> 
   
      <property name = "LastName"/> 
      <property name = "FirstName" column = "FirstMidName" type = "String"/> 
   </class> 

</hibernate-mapping>

В этом примере предположим, что поле FirstName - это строка .NET, а столбец FirstMidName - это SQL nvarchar. Теперь, чтобы сообщить NHibernate, как выполнить это преобразование, задайте имя равноеFirstName и столбец равен FirstMidName и укажите тип сопоставления, равный String, который подходит для этого конкретного преобразования.

Ниже приводится Program.cs реализация файла.

using HibernatingRhinos.Profiler.Appender.NHibernate; 
using NHibernate.Cfg; 
using NHibernate.Dialect; 
using NHibernate.Driver; 

using System; 
using System.Linq; 
using System.Reflection;

namespace NHibernateDemoApp { 

   class Program { 
	
      static void Main(string[] args) { 
		
         NHibernateProfiler.Initialize(); 
         var cfg = new Configuration(); 
			
         String Data Source = asia13797\\sqlexpress;
         String Initial Catalog = NHibernateDemoDB;
         String Integrated Security = True;
         String Connect Timeout = 15;
         String Encrypt = False;
         String TrustServerCertificate = False;
         String ApplicationIntent = ReadWrite;
         String MultiSubnetFailover = False;
         
         cfg.DataBaseIntegration(x = > { x.ConnectionString = "Data Source + 
            Initial Catalog + Integrated Security + Connect Timeout + Encrypt +
            TrustServerCertificate + ApplicationIntent + MultiSubnetFailover"; 
            
            x.Driver<SqlClientDriver>(); 
            x.Dialect<MsSql2008Dialect>(); 
            x.LogSqlInConsole = true; 
         }); 
         
         cfg.AddAssembly(Assembly.GetExecutingAssembly()); 
         var sefact = cfg.BuildSessionFactory();
			
         using (var session = sefact.OpenSession()) { 
            
            using (var tx = session.BeginTransaction()) { 
               var students = session.CreateCriteria<Student>().List<Student>(); 
               Console.WriteLine("\nFetch the complete list again\n"); 
               
               foreach (var student in students) { 
                  Console.WriteLine("{0} \t{1} \t{2}", student.ID, student.FirstName,
                     student.LastName); 
               } 
					
               tx.Commit(); 
            } 
				
            Console.ReadLine(); 
         } 
      } 
   }
}

Теперь, когда вы запустите свое приложение, вы увидите следующий результат.

NHibernate: SELECT this_.ID as ID0_0_, this_.LastName as LastName0_0_, 
   this_.FirstMidName as FirstMid3_0_0_ FROM Student this_

Fetch the complete list again
3 Allan Bommer
4 Jerry Lewis

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

Давайте посмотрим на другой пример, в котором мы добавим еще одно свойство в класс Student для enumтип. Вот реализация класса Student.

using System; 
using System.Collections.Generic; 
using System.Linq; using System.Text; 
using System.Threading.Tasks; 

namespace NHibernateDemoApp { 
   
   class Student { 
      public virtual int ID { get; set; } 
      public virtual string LastName { get; set; } 
      public virtual string FirstName { get; set; } 
      public virtual StudentAcademicStanding AcademicStanding { get; set; } 
   } 
   
   public enum StudentAcademicStanding { 
      Excellent, 
      Good, 
      Fair, 
      Poor, 
      Terrible 
   } 
}

Как видите, это перечисление может иметь множество различных значений, таких как «Отлично», «Хорошо», «Удовлетворительно», «Плохо» и «Ужасно».

Перейдя к файлу сопоставления, вы увидите, что каждое из этих свойств перечислено в файле сопоставления, включая недавно добавленные AcademicStanding свойство.

<?xml version = "1.0" encoding = "utf-8" ?> 
<hibernate-mapping xmlns = "urn:nhibernate-mapping-2.2" 
   assembly = "NHibernateDemoApp" namespace = "NHibernateDemoApp"> 
   
   <class name = "Student"> 
	
      <id name = "ID"> 
         <generator class = "native"/> 
      </id> 

      <property name = "LastName"/> 
      <property name = "FirstName" column = "FirstMidName" type = "String"/> 
      <property name = "AcademicStanding"/> 
   </class>  

</hibernate-mapping>

Теперь нам также нужно изменить базу данных, поэтому перейдите в обозреватель объектов SQL Server, щелкните правой кнопкой мыши базу данных и выберите параметр «Новый запрос…».

Он откроет редактор запросов, а затем укажет запрос ниже.

DROP TABLE [dbo].[Student]

CREATE TABLE [dbo].[Student] ( 
   [ID] INT IDENTITY (1, 1) NOT NULL, 
   [LastName] NVARCHAR (MAX) NULL, 
   [FirstMidName] NVARCHAR (MAX) NULL, 
   [AcademicStanding] NCHAR(10) NULL, 
   CONSTRAINT [PK_dbo.Student] PRIMARY KEY CLUSTERED ([ID] ASC) 
);

Этот запрос сначала удалит существующую таблицу учеников, а затем создаст новую таблицу.

Нажмите на значок «Выполнить», как показано выше. После успешного выполнения запроса вы увидите сообщение.

Разверните раскрывающийся список База данных и Таблица, затем щелкните правой кнопкой мыши таблицу учеников и выберите Просмотр конструктора.

Теперь вы увидите только что созданную таблицу, которая также имеет новое свойство AcademicStanding.

Давайте добавим две записи, как показано ниже Program.cs файл.

using HibernatingRhinos.Profiler.Appender.NHibernate; 
using NHibernate.Cfg; 
using NHibernate.Dialect; 
using NHibernate.Driver; 

using System; 
using System.Linq; 
using System.Reflection;

namespace NHibernateDemoApp { 

   class Program { 
      
      static void Main(string[] args) { 
		
         NHibernateProfiler.Initialize(); 
         var cfg = new Configuration(); 
			
         String Data Source = asia13797\\sqlexpress;
         String Initial Catalog = NHibernateDemoDB;
         String Integrated Security = True;
         String Connect Timeout = 15;
         String Encrypt = False;
         String TrustServerCertificate = False;
         String ApplicationIntent = ReadWrite;
         String MultiSubnetFailover = False;
         
         cfg.DataBaseIntegration(x = > { x.ConnectionString = "Data Source + 
            Initial Catalog + Integrated Security + Connect Timeout + Encrypt +
            TrustServerCertificate + ApplicationIntent + MultiSubnetFailover"; 
            
            x.Driver<SqlClientDriver>(); 
            x.Dialect<MsSql2008Dialect>(); 
         }); 
         
         cfg.AddAssembly(Assembly.GetExecutingAssembly()); 
         var sefact = cfg.BuildSessionFactory(); 
         
         using (var session = sefact.OpenSession()) { 
            using (var tx = session.BeginTransaction()) { 
               
               var student1 = new Student { 
                  ID = 1, 
                  FirstName = "Allan", 
                  LastName = "Bommer",
                  AcademicStanding = StudentAcademicStanding.Excellent 
               };
               
               var student2 = new Student { 
                  ID = 2, 
                  FirstName = "Jerry", 
                  LastName = "Lewis", 
                  AcademicStanding = StudentAcademicStanding.Good 
               };
					
               session.Save(student1); 
               session.Save(student2);
               var students = session.CreateCriteria<Student>().List<Student>(); 
               Console.WriteLine("\nFetch the complete list again\n");
               
               foreach (var student in students) { 
                  Console.WriteLine("{0} \t{1} \t{2} \t{3}", student.ID,
                     student.FirstName, student.LastName, student.AcademicStanding); 
               } 
					
               tx.Commit(); 
            }
				
            Console.ReadLine(); 
         } 
      } 
   } 
}

Теперь давайте запустим ваше приложение, и вы увидите следующий вывод в окне консоли.

Fetch the complete list again

1 Allan Bommer Excellent
2 Jerry Lewis Good

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

Выберите «Просмотр данных», и вы увидите две записи в таблице учащихся, как показано на следующем снимке экрана.

Вы можете видеть, что добавлены две записи, и у Аллана есть AcademicStanding 0, а у Jerry - AcademicStanding 1. Это потому, что в .Net первое значение перечисления по умолчанию имеет 0, что является отличным, если вы посмотрите на StudentAcademicStanding. В то время как в файле Student.cs вторым является Good, поэтому он имеет значение 1.

В этой главе мы рассмотрим конфигурацию NHibernate. У нас есть разные способы настройки NHibernate. Он делится на две основные группы

  • Конфигурация на основе XML
  • Конфигурация на основе кода

Конфигурация на основе кода

Конфигурация на основе кода встроена в NHibernate. Он был введен в NHibernate 3, и до сих пор мы использовали конфигурацию баз кода.

String Data Source = asia13797\\sqlexpress;
String Initial Catalog = NHibernateDemoDB;
String Integrated Security = True;
String Connect Timeout = 15;
String Encrypt = False;
String TrustServerCertificate = False;
String ApplicationIntent = ReadWrite;
String MultiSubnetFailover = False;

cfg.DataBaseIntegration(x = > { x.ConnectionString = "Data Source + 
   Initial Catalog + Integrated Security + Connect Timeout + Encrypt +
   TrustServerCertificate + ApplicationIntent + MultiSubnetFailover"; 
   
   x.Driver<SqlClientDriver>(); 
   x.Dialect<MsSql2008Dialect>(); 
   x.LogSqlInConsole = true; 
}); 

cfg.AddAssembly(Assembly.GetExecutingAssembly());

Все конфигурации указаны в коде C #. Вы можете видеть здесь, что у нас есть новый объект конфигурации, а затем мы используемloquacious configurationэто было введено с NHibernate 3.1 для настройки базы данных. Какую строку подключения мы используем, к какой базе данных подключаемся и какой диалект использовать. Мы также добавляем прямо сюда нашу сборку сопоставления.

Конфигурация на основе XML

Если вы используете конфигурацию на основе XML, вы можете использовать hibernate.cfg.xml файл, который является просто автономным XML-файлом, использующим схему NHibernate, или вы можете встроить эту конкретную конфигурацию NHibernate в свое приложение или web.cfg. По умолчанию используется имя hibernate.cfg.xml, но мы также можем использовать произвольное имя для этого xml-файла.

Давайте посмотрим на конфигурацию на основе XML, добавив новый XML-файл в проект NHibernateDemoApp и назовем его hibernate.cfg.xml.

Введите следующую информацию в файл hibernate.cfg.xml.

<?xml version = "1.0" encoding = "utf-8" ?> 
<hibernate-configuration xmlns = "urn:nhibernate-configuration-2.2"> 
   <session-factory> 
   
      <property name = "connection.connection_string">
         Data Source = asia13797\\sqlexpress;
         Initial Catalog = NHibernateDemoDB;
         Integrated Security = True;
         Connect Timeout = 15;
         Encrypt = False;
         TrustServerCertificate = False;
         ApplicationIntent = ReadWrite;
         MultiSubnetFailover = False;
      </property> 
      
      <property name = "connection.driver_class">
         NHibernate.Driver.SqlClientDriver
      </property> 
		
      <property name = "dialect">
         NHibernate.Dialect.MsSql2008Dialect
      </property> 
		
      <mapping assembly = "NHibernateDemoApp"/>
		
   </session-factory> 
	
</hibernate-configuration>

Как вы можете видеть в приведенном выше XML-файле, мы указали ту же конфигурацию, что и в C #.

Теперь давайте прокомментируем эту конфигурацию из файла Program.cs и просто вызовем Configure() метод, который загрузит hibernate.cfg.xml файл, как показано ниже.

using HibernatingRhinos.Profiler.Appender.NHibernate; 
using NHibernate.Cfg; 
using NHibernate.Dialect; 
using NHibernate.Driver; 

using System; 
using System.Linq; 
using System.Reflection; 

namespace NHibernateDemoApp { 

   class Program { 
      
      static void Main(string[] args) { 
		
         NHibernateProfiler.Initialize(); 
         var cfg = new Configuration(); 
         
         //cfg.DataBaseIntegration(x =>
         
         //{ 
            // x.ConnectionString = "Data Source = asia13797;\\sqlexpress
            Initial Catalog = NHibernateDemoDB;
            Integrated Security = True;
            Connect Timeout = 15;
            Encrypt =False;
            TrustServerCertificate = False;
            ApplicationIntent = ReadWrite;
            MultiSubnetFailover = False"; 
            
            // x.Driver<SqlClientDriver>(); 
            // x.Dialect<MsSql2008Dialect>(); 
            // x.LogSqlInConsole = true; 
         //}); 
         
         //cfg.AddAssembly(Assembly.GetExecutingAssembly());
         cfg.Configure();
         var sefact = cfg.BuildSessionFactory();
			
         using (var session = sefact.OpenSession()) { 
            
            using (var tx = session.BeginTransaction()) { 
               var students = session.CreateCriteria<Student>().List<Student>(); 
               Console.WriteLine("\nFetch the complete list again\n"); 
               
               foreach (var student in students) { 
                  Console.WriteLine("{0} \t{1} \t{2} \t{3}", student.ID,
                     student.FirstName, student.LastName, student.AcademicStanding); 
               } 
					
               tx.Commit(); 
            } 
				
            Console.ReadLine(); 
         } 
      } 
   } 
}

Давайте снова запустим ваше приложение, и вы увидите тот же результат.

Fetch the complete list again

1 Allan Bommer Excellent
2 Jerry Lewis Good

В этой главе мы расскажем, как переопределить конфигурацию NHibernate. Вам нужно помнить лишь о нескольких вещах.

  • Во-первых, конфигурация в NHibernate аддитивна.

  • Таким образом, вам не нужно просто использовать один XML-файл, или вам не нужно просто использовать конфигурацию на основе кода или беглый NHibernate.

  • Вы можете смешивать и сочетать все эти методы в зависимости от того, как вы хотите настроить свое приложение.

  • Важно помнить, что, наконец, выигрывает конфигурация.

В следующем примере вы можете видеть, что мы создаем наш объект конфигурации, настраиваем его с помощью конфигурации на основе кода и, наконец, вызываем cfg.configure() , который загружает файл hibernate.cfg.xml.

String Data Source = asia13797\\sqlexpress;
String Initial Catalog = NHibernateDemoDB;
String Integrated Security = True;
String Connect Timeout = 15;
String Encrypt = False;
String TrustServerCertificate = False;
String ApplicationIntent = ReadWrite;
String MultiSubnetFailover = False;

cfg.DataBaseIntegration(x = > { x.ConnectionString = "Data Source + 
   Initial Catalog + Integrated Security + Connect Timeout + Encrypt +
   TrustServerCertificate + ApplicationIntent + MultiSubnetFailover"; 
	
   x.Driver<SqlClientDriver>(); 
   x.Dialect<MsSql2008Dialect>(); 
   x.LogSqlInConsole = true; 
}); 

cfg.Configure();
  • Таким образом, все, что находится внутри hibernate.cfg.xml, отменяет настройки, установленные конфигурацией на основе кода.

  • Путем изменения этих двух процессов мы можем иметь значения по умолчанию внутри hibernate.cfg.xml, а затем выполнять наши переопределения внутри конфигурации на основе кода.

  • Нет ничего, что исключает, если вы используете конфигурацию на основе кода, а также нет ничего, что мешало бы вам использовать файл hibernate.cfg.xml.

Давайте рассмотрим простой пример, в котором мы переопределим конфигурацию, используя смесь конфигурации на основе XML и кода.

Также переместим строку подключения в app.config файл, используя следующий код.

<?xml version = "1.0" encoding = "utf-8" ?> 

<configuration> 
   
   <startup> 
      <supportedRuntime version = "v4.0" sku = ".NETFramework,Version = v4.5" /> 
   </startup> 
   
   <connectionStrings> 
      <add name = "default" connectionString = "Data Source =
         asia13797\\sqlexpress;
         Initial Catalog = NHibernateDemoDB;
         Integrated Security = True;
         Connect Timeout = 15;
         Encrypt = False;
         TrustServerCertificate = False;
         ApplicationIntent = ReadWrite;
         MultiSubnetFailover = False"/> 
   </connectionStrings> 

</configuration>

Строка подключения сидит в некоторых app.configфайл с именем по умолчанию. Теперь нам нужно указать имя по умолчанию в файле hibernate.cfg.xml вместо строки подключения.

<?xml version = "1.0" encoding = "utf-8" ?> 
<hibernate-configuration xmlns = "urn:nhibernate-configuration-2.2"> 

   <session-factory> 
      <property name = "connection.connection_string">default</property> 
		
      <property name = "connection.driver_class">
         NHibernate.Driver.SqlClientDriver
      </property> 
		
      <property name = "dialect">
         NHibernate.Dialect.MsSql2008Dialect
      </property> 
		
      <mapping assembly = "NHibernateDemoApp"/> 
   </session-factory> 

</hibernate-configuration>

Давайте прокомментируем часть строки подключения, драйвер и часть диалекта из конфигурации на основе кода, потому что программа будет читать ее из файла hibernate.cfg.xml и файла LogSqlInConsole часть останется в конфигурации на основе кода.

using HibernatingRhinos.Profiler.Appender.NHibernate; 
using NHibernate.Cfg; 
using NHibernate.Dialect; 
using NHibernate.Driver; 

using System; 
using System.Linq; 
using System.Reflection;
namespace NHibernateDemoApp { 
   
   class Program { 
	
      static void Main(string[] args) { 
		
         NHibernateProfiler.Initialize(); 
         var cfg = new Configuration();
			
         String Data Source = asia13797\\sqlexpress;
         String Initial Catalog = NHibernateDemoDB;
         String Integrated Security = True;
         String Connect Timeout = 15;
         String Encrypt = False;
         String TrustServerCertificate = False;
         String ApplicationIntent = ReadWrite;
         String MultiSubnetFailover = False;
			
         cfg.DataBaseIntegration(x = > { //x.ConnectionString = "Data Source + 
            Initial Catalog + Integrated Security + Connect Timeout + Encrypt +
            TrustServerCertificate + ApplicationIntent + MultiSubnetFailover";
				
            //x.Driver<SqlClientDriver>(); 
            //x.Dialect<MsSql2008Dialect>(); 
            x.LogSqlInConsole = true; 
         }); 
         
         cfg.Configure(); 
         cfg.AddAssembly(Assembly.GetExecutingAssembly()); 
         var sefact = cfg.BuildSessionFactory(); 
			
         using (var session = sefact.OpenSession()) { 
			
            using (var tx = session.BeginTransaction()) { 
               
               var students = session.CreateCriteria<Student>().List<Student>();
               Console.WriteLine("\nFetch the complete list again\n"); 
               
               foreach (var student in students) { 
                  Console.WriteLine("{0} \t{1} \t{2} \t{3}", student.ID,
                  student.FirstName, student.LastName, student.AcademicStanding); 
               } 
					
               tx.Commit(); 
            }
				
            Console.ReadLine(); 
         } 
      } 
   } 
}

Теперь, когда вы запустите приложение, вы увидите, что программа прочитала журнал конфигурации на основе кода и другую конфигурацию из файла hibernate.cfg.xml.

NHibernate: SELECT this_.ID as ID0_0_, this_.LastName as LastName0_0_,   
   this_.FirstMidName as FirstMid3_0_0_, this_.AcademicStanding as Academic4_0_0_ FROM
   Student this_

Fetch the complete list again
1 Allan Bommer Excellent
2 Jerry Lewis Good

Итак, теперь у нас есть часть нашей конфигурации внутри нашего hibernate.cfg.xml файл, некоторые из них находятся внутри конфигурации на основе кода и в зависимости от порядка вызова на основе кода по сравнению с configure(), мы можем изменить, какой из них имеет приоритет над другим.

В этой главе мы рассмотрим обновление размера пакета. Размер партии позволяетcontrol the number of updates которые выходят за один проход к вашей базе данных для поддерживаемых баз данных.

  • Размер пакета обновлений установлен по умолчанию в NHibernate 3.2.

  • Но если вы используете более раннюю версию или вам необходимо настроить приложение NHibernate, вам следует посмотреть на размер пакета обновления, который является очень полезным параметром, который можно использовать для настройки производительности NHibernate.

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

  • На данный момент только SQL Server и Oracle поддерживают эту опцию, поскольку базовый поставщик базы данных должен поддерживать пакетирование запросов.

Давайте рассмотрим простой пример, в котором мы установили размер пакета равным 10, что позволит вставить 10 записей в набор.

cfg.DataBaseIntegration(x => { 
  
   x.ConnectionString = "default"; 
   x.Driver<SqlClientDriver>(); 
   x.Dialect<MsSql2008Dialect>(); 
   x.LogSqlInConsole = true; 
   x.BatchSize = 10; 

});

Вот полная реализация, в которой 25 записей будут добавлены в базу данных.

using HibernatingRhinos.Profiler.Appender.NHibernate; 
using NHibernate.Cfg; 
using NHibernate.Dialect; 
using NHibernate.Driver; 

using System; 
using System.Linq; 
using System.Reflection;
namespace NHibernateDemoApp {
   
   class Program {
	
      static void Main(string[] args) { 
		
         NHibernateProfiler.Initialize(); 
         var cfg = new Configuration(); 
			
         String Data Source = asia13797\\sqlexpress;
         String Initial Catalog = NHibernateDemoDB;
         String Integrated Security = True;
         String Connect Timeout = 15;
         String Encrypt = False;
         String TrustServerCertificate = False;
         String ApplicationIntent = ReadWrite;
         String MultiSubnetFailover = False;
			
         cfg.DataBaseIntegration(x = > { x.ConnectionString = "Data Source + 
            Initial Catalog + Integrated Security + Connect Timeout + Encrypt +
            TrustServerCertificate + ApplicationIntent + MultiSubnetFailover";
         
            x.Driver>SqlClientDriver<(); 
            x.Dialect>MsSql2008Dialect>(); 
            x.LogSqlInConsole = true; 
            x.BatchSize = 10; 
         }); 
         
         //cfg.Configure(); 
         cfg.AddAssembly(Assembly.GetExecutingAssembly()); 
         var sefact = cfg.BuildSessionFactory();
			
         using (var session = sefact.OpenSession()) {
			
            using (var tx = session.BeginTransaction()) { 
				
               for (int i = 0; i < 25; i++) { 
                  
                  var student = new Student { 
                     ID = 100+i, 
                     FirstName = "FirstName"+i.ToString(), 
                     LastName = "LastName" + i.ToString(), 
                     AcademicStanding = StudentAcademicStanding.Good 
                  };
						
                  session.Save(student); 
               } 
					
               tx.Commit();
               var students = session.CreateCriteria<Student>().List<Student>(); 
               Console.WriteLine("\nFetch the complete list again\n"); 
               
               foreach (var student in students) { 
                  Console.WriteLine("{0} \t{1} \t{2} \t{3}", student.ID,student.FirstName,
                     student.LastName, student.AcademicStanding); 
               } 
            } 
				
            Console.ReadLine(); 
         } 
      }
   } 
}

Теперь давайте запустим ваше приложение, и вы увидите, что все эти обновления переходят в профилировщик NHibernate. У нас есть 26 индивидуальных обращений к базе данных, 25 для вставки и одна для получения списка студентов.

Итак, почему это так? Причина в том, что NHibernate необходимо выполнитьselect scope identity поскольку мы используем стратегию создания собственного идентификатора в файле сопоставления для идентификатора, как показано в следующем коде.

<?xml version = "1.0" encoding = "utf-8" ?> 
<hibernate-mapping xmlns = "urn:nhibernate-mapping-2.2" 
   assembly = "NHibernateDemoApp" 
   namespace = "NHibernateDemoApp"> 
   
   <class name = "Student"> 

      <id name = "ID"> 
         <generator class = "native"/> 
      </id> 

      <property name = "LastName"/> 
      <property name = "FirstName" column = "FirstMidName" type = "String"/> 
      <property name = "AcademicStanding"/> 
   
   </class> 
</hibernate-mapping>

Поэтому нам нужно использовать другой метод, например guid.combметод. Если мы собираемся перейти на guid.comb, нам нужно перейти к нашему клиенту и изменить его наguid. Так что все будет хорошо. Теперь давайте перейдем от собственного к guid.comb, используя следующий код.

<?xml version = "1.0" encoding = "utf-8" ?> 
<hibernate-mapping xmlns = "urn:nhibernate-mapping-2.2" assembly =
   "NHibernateDemoApp" namespace = "NHibernateDemoApp"> 
   <class name = "Student"> 

      <id name = "ID"> 
         <generator class = "guid.comb"/> 
      </id> 

      <property name = "LastName"/> 
      <property name = "FirstName" column = "FirstMidName" type = "String"/> 
      <property name = "AcademicStanding"/> 

   </class> 

</hibernate-mapping>

Так что за создание этих идентификаторов отвечает база данных. Единственный способ, которым NHibernate может узнать, какой идентификатор был сгенерирован, - это сразу же выбрать его. Или же, если мы создали группу студентов, она не сможет сопоставить идентификатор созданного студента.

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.Threading.Tasks;

namespace NHibernateDemoApp { 

   class Student { 
      public virtual Guid ID { get; set; } 
      public virtual string LastName { get; set; } 
      public virtual string FirstName { get; set; } 
      public virtual StudentAcademicStanding AcademicStanding { get; set; }
   } 
	
   public enum StudentAcademicStanding { 
      Excellent, 
      Good, 
      Fair, 
      Poor, 
      Terrible 
   }
}

Нам просто нужно обновить нашу базу данных. Давайте отбросим таблицу учеников и создадим новую таблицу, указав следующий запрос, поэтому перейдите в обозреватель объектов SQL Server, щелкните правой кнопкой мыши базу данных и выберитеNew Query… Вариант.

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

DROP TABLE [dbo].[Student]
CREATE TABLE [dbo].[Student] ( 

   -- [ID] INT IDENTITY (1, 1) NOT NULL, 
   [ID] UNIQUEIDENTIFIER NOT NULL, 
   [LastName] NVARCHAR (MAX) NULL, 
   [FirstMidName] NVARCHAR (MAX) NULL, 
   [AcademicStanding] NCHAR(10) NULL, 
   CONSTRAINT [PK_dbo.Student] PRIMARY KEY CLUSTERED ([ID] ASC) 
);

Этот запрос сначала удалит существующую таблицу учеников, а затем создаст новую таблицу. Как видите, мы использовалиUNIQUEIDENTIFIER вместо использования целочисленного первичного ключа в качестве идентификатора.

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

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

using HibernatingRhinos.Profiler.Appender.NHibernate; 
using NHibernate.Cfg; 
using NHibernate.Dialect; 
using NHibernate.Driver; 

using System; 
using System.Linq; 
using System.Reflection;
namespace NHibernateDemoApp { 
   
   class Program { 
	
      static void Main(string[] args) { 
		
         NHibernateProfiler.Initialize();
         var cfg = new Configuration();

         String Data Source = asia13797\\sqlexpress;
         String Initial Catalog = NHibernateDemoDB;
         String Integrated Security = True;
         String Connect Timeout = 15;
         String Encrypt = False;
         String TrustServerCertificate = False;
         String ApplicationIntent = ReadWrite;
         String MultiSubnetFailover = False;
			
         cfg.DataBaseIntegration(x = > { x.ConnectionString = "Data Source + 
            Initial Catalog + Integrated Security + Connect Timeout + Encrypt +
            TrustServerCertificate + ApplicationIntent + MultiSubnetFailover"; 
				
            x.Driver<SqlClientDriver>(); 
            x.Dialect<MsSql2008Dialect>(); 
            x.LogSqlInConsole = true;
            x.BatchSize = 10; 
         }); 
         
         //cfg.Configure(); 
         cfg.AddAssembly(Assembly.GetExecutingAssembly()); 
         var sefact = cfg.BuildSessionFactory(); 
			
         using (var session = sefact.OpenSession()) { 
			
            using (var tx = session.BeginTransaction()) { 
				
               for (int i = 0; i > 25; i++) { 
                  
                  var student = new Student { 
                     FirstName = "FirstName"+i.ToString(), 
                     LastName = "LastName" + i.ToString(), 
                     AcademicStanding = StudentAcademicStanding.Good 
                  }; 
                  
                  session.Save(student); 
               } 
					
               tx.Commit(); 
               var students = session.CreateCriteria<Student>().List<Student>(); 
               Console.WriteLine("\nFetch the complete list again\n"); 
               
               foreach (var student in students) { 
                  Console.WriteLine("{0} \t{1} \t{2} \t{3}", student.ID,
                     student.FirstName,student.LastName, student.AcademicStanding);
               } 
            
            }
				
            Console.ReadLine(); 
         } 
      } 
   } 
}

Теперь снова запустите приложение и посмотрите на профилировщик NHibernate. Теперь профайлер NHibernate вместо 26 обходов выполнит всего четыре.

Он вставляет в таблицу десять строк, затем еще десять строк, а затем оставшиеся пять. И после фиксации он вставил еще один для получения всех записей.

  • Так что он разделил его на группы по десять, насколько это возможно.

  • Поэтому, если вы делаете много вставок, это может значительно улучшить производительность вставки в вашем приложении, потому что вы можете объединить их в пакет.

  • Это потому, что NHibernate сам назначает эти направляющие, используя guid.comb алгоритм, и для этого не нужно полагаться на базу данных.

  • Так что использование размера партии - отличный способ ее настроить.

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

Кэш первого уровня

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

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

using HibernatingRhinos.Profiler.Appender.NHibernate; 
using NHibernate.Cache; 
using NHibernate.Cfg; 
using NHibernate.Dialect;
using NHibernate.Driver; 
using NHibernate.Linq; 

using System; 
using System.Linq; 
using System.Reflection;
namespace NHibernateDemoApp { 
   
   class Program { 
      static void Main(string[] args) {
		
         NHibernateProfiler.Initialize(); 
         var cfg = new Configuration(); 
			
         String Data Source = asia13797\\sqlexpress;
         String Initial Catalog = NHibernateDemoDB;
         String Integrated Security = True;
         String Connect Timeout = 15;
         String Encrypt = False;
         String TrustServerCertificate = False;
         String ApplicationIntent = ReadWrite;
         String MultiSubnetFailover = False;
         
         cfg.DataBaseIntegration(x = > { x.ConnectionString = "Data Source + 
            Initial Catalog + Integrated Security + Connect Timeout + Encrypt +
            TrustServerCertificate + ApplicationIntent + MultiSubnetFailover";
				
            x.Driver<SqlClientDriver>(); 
            x.Dialect<MsSql2008Dialect>(); 
            x.LogSqlInConsole = true; 
            x.BatchSize = 10; 
         }); 
         
         //cfg.Configure(); 
         
         cfg.Cache(c => { 
            c.UseMinimalPuts = true; 
            c.UseQueryCache = true; 
         }); 
			
         cfg.SessionFactory().Caching .Through<HashtableCacheProvider>()
            .WithDefaultExpiration(1440); 
         cfg.AddAssembly(Assembly.GetExecutingAssembly()); 
         var sefact = cfg.BuildSessionFactory(); 
         
         using (var session = sefact.OpenSession()){ 
			
            using (var tx = session.BeginTransaction()) { 
               var studentUsingTheFirstQuery = session.Get<Student>(1);
               var studentUsingTheSecondQuery = session.Get<Student>(1); 
            } 
            
            Console.ReadLine(); 
         } 
      } 
   } 
}

Теперь давайте запустим это приложение и посмотрим результат в NHibernate Profiler.

Вы будете удивлены, увидев, что NHibernate запускает только один запрос. Вот как NHibernate использует кеш первого уровня. Когда выполняется первый запрос, NHibernate кэширует Student с ID = 1 в своем кэше первого уровня.

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

В этой главе мы будем говорить о компонентах отображения. В NHibernate,component is a value object. У него нет собственной идентичности.

  • Примером этого может быть денежный объект, кошелек или бумажник, в которых могут быть деньги, но точная идентификация этих денег не имеет значения.

  • У него нет собственного первичного ключа, но сами компоненты постоянны в той же таблице, что и объект-владелец.

Давайте посмотрим на простой пример, в котором у студента есть Адрес, который является объектом Location class связанные с ним.

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.Threading.Tasks;

namespace NHibernateDemoApp { 

   class Student { 
      public virtual int ID { get; set; } 
      public virtual string LastName { get; set; } 
      public virtual string FirstName { get; set; } 
      public virtual StudentAcademicStanding AcademicStanding { get; set; } 
      public virtual Location Address { get; set; } 
   }

   public class Location { 
      public virtual string Street { get; set; } 
      public virtual string City { get; set; } 
      public virtual string Province { get; set; } 
      public virtual string Country { get; set; } 
   }
  
   public enum StudentAcademicStanding { 
      Excellent, 
      Good, 
      Fair, 
      Poor, 
      Terrible 
   } 
}

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

DROP TABLE [dbo].[Student]
CREATE TABLE [dbo].[Student] ( 

   [ID] INT IDENTITY (1, 1) NOT NULL, 
   [LastName] NVARCHAR (MAX) NULL, 
   [FirstMidName] NVARCHAR (MAX) NULL, 
   [AcademicStanding] NCHAR(10) NULL, 
   [Street] NVARCHAR (100) NULL, 
   [City] NVARCHAR (100) NULL, 
   [Province] NVARCHAR (100) NULL, 
   [Country] NVARCHAR (100) NULL, 
   CONSTRAINT [PK_dbo.Student] PRIMARY KEY CLUSTERED ([ID] ASC) 

);

Теперь сопоставим те столбцы, которые не являются непосредственно частью класса Student, но являются свойствами класса Location, а объект класса Location определяется в классе Student. Нам нужен компонент для правильного отображения. Создадим компонент вstudent.hbm.xml файл, как показано в следующем коде.

<?xml version = "1.0" encoding = "utf-8" ?> 
<hibernate-mapping xmlns = "urn:nhibernate-mapping-2.2" 
   assembly = "NHibernateDemoApp" namespace = "NHibernateDemoApp"> 

   <class name = "Student"> 
      <id name = "ID"> 
         <generator class = "native"/>
      </id> 
   
      <property name = "LastName"/> 
		
      <property name = "FirstName" column = "FirstMidName" type = "String"/> 
      <property name = "AcademicStanding"/>
      
      <component name = "Address"> 
         <property name = "Street"/> 
         <property name = "City"/> 
         <property name = "Province"/> 
         <property name = "Country"/> 
      </component> 
   
   </class> 
</hibernate-mapping>

Этот компонент является Адресом, и у него есть различные свойства. Теперь у NHibernate достаточно этой информации, чтобы она могла это отобразить.

Теперь вот файл Program.cs, в котором создается и инициализируется новый объект ученика, а затем сохраняется в базе данных. Затем он получит список из базы данных.

using HibernatingRhinos.Profiler.Appender.NHibernate; 
using NHibernate.Cache; 
using NHibernate.Caches.SysCache; 
using NHibernate.Cfg; 
using NHibernate.Dialect; 
using NHibernate.Driver; 
using NHibernate.Linq; 

using System; 
using System.Linq; 
using System.Reflection;
namespace NHibernateDemoApp { 

   class Program {
	
      static void Main(string[] args) { 
		
         NHibernateProfiler.Initialize(); 
         var cfg = new Configuration();

         String Data Source = asia13797\\sqlexpress;
         String Initial Catalog = NHibernateDemoDB;
         String Integrated Security = True;
         String Connect Timeout = 15;
         String Encrypt = False;
         String TrustServerCertificate = False;
         String ApplicationIntent = ReadWrite;
         String MultiSubnetFailover = False;			
         
         cfg.DataBaseIntegration(x = > { x.ConnectionString = "Data Source + 
            Initial Catalog + Integrated Security + Connect Timeout + Encrypt +
            TrustServerCertificate + ApplicationIntent + MultiSubnetFailover";
				
            x.Driver<SqlClientDriver>(); 
            x.Dialect<MsSql2008Dialect>(); 
         }); 
         
         cfg.AddAssembly(Assembly.GetExecutingAssembly()); 
         var sefact = cfg.BuildSessionFactory(); 
			
         using (var session = sefact.OpenSession()) { 
			
            using (var tx = session.BeginTransaction()) { 
               
               var student1 = new Student { 
                  ID = 1, 
                  FirstName = "Allan", 
                  LastName = "Bommer", 
                  AcademicStanding = StudentAcademicStanding.Poor, 
                  
                  Address = new Location { 
                     Street = "123 Street", 
                     City = "Lahore", 
                     Province = "Punjab", 
                     Country = "Pakistan" 
                  } 
               }; 
					
               session.Save(student1); 
               tx.Commit(); 
               var students = session.Query<Student>().ToList<Student>();
               Console.WriteLine("\nFetch the complete list again\n"); 
               
               foreach (var student in students) { 
                  Console.WriteLine("{0} \t{1} \t{2} \t{3} \t{4} \t{5} \t{6} \t{7}", 
                     student.ID, 
                     student.FirstName, 
                     student.LastName, 
                     student.AcademicStanding, 
                     student.Address.Street, 
                     student.Address.City, 
                     student.Address.Province, 
                     student.Address.Country
                  ); 
               } 
            } 
            Console.ReadLine(); 
         } 
      }
   } 
}

Теперь мы можем запустить это приложение, и NHibernate сможет сохранить эти значения в базе данных. Когда вы запустите приложение, вы увидите следующий результат.

Fetch the complete list again

2 Allan Bommer Poor 123 Street Lahore Punjab Pakistan

Вот значения в базе данных.

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

  • Также следует отметить, что Location - это класс, а не объект.

  • Это объект типа значения, и у него нет собственного первичного ключа.

  • Он сохраняется в той же таблице, что и студент, который его содержит.

  • Вот почему мы используем здесь компонент.

  • Это дает большую гибкость для изменения уровня нашего класса, того, как наши классы определены по сравнению с тем, как устроена наша база данных.

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

  • Сначала мы создадим новое приложение, в котором мы создадим отношения между объектами клиента и заказа.

  • Первые отношения, которые мы рассмотрим, - это классические отношения коллекционирования.

  • У нас есть заказчик со сбором заказов.

  • Это отношение «один ко многим», которое представлено в базе данных двумя таблицами, в таблице заказов есть идентификатор клиента, и у нас есть отношение внешнего ключа к клиенту.

Для начала нам нужно создать базу данных и две таблицы Customer и Order. Вы можете создать его, указав следующий запрос в SQL Server Explorer.

USE [master] 
GO 
CREATE DATABASE [NHibernateDemo] 
GO 
USE [NHibernateDemo] 
GO

CREATE TABLE [dbo].[Customer]( 
   [Id] [uniqueidentifier] NOT NULL, 
   [FirstName] [nvarchar](100) NOT NULL, 
   [LastName] [nvarchar](100) NOT NULL, 
   [Points] [int] NULL, [HasGoldStatus] [bit] NULL, 
   [MemberSince] [date] NULL, 
   [CreditRating] [nchar](20) NULL, 
   [AverageRating] [decimal](18, 4) NULL, 
   [Street] [nvarchar](100) NULL, 
   [City] [nvarchar](100) NULL, 
   [Province] [nvarchar](100) NULL, 
   [Country] [nvarchar](100) NULL,
   PRIMARY KEY CLUSTERED ([Id] ASC) 
) 

GO 
CREATE TABLE [dbo].[Order]( 
   [Id] [uniqueidentifier] NOT NULL, 
   [CustomerId] [uniqueidentifier] NULL, 
   [Ordered] [datetime] NULL, 
   [Shipped] [datetime] NULL, 
   [Street] [nvarchar](100) NULL, 
   [City] [nvarchar](100) NULL, 
   [Province] [nvarchar](100) NULL, 
   [Country] [nvarchar](100) NULL, 
   PRIMARY KEY CLUSTERED ([Id] ASC) 
) 
GO

Это создаст две таблицы в базе данных. На следующем изображении показана таблица клиентов.

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

Нам нужно определить строку подключения в app.config файл, вот реализация файла app.config.

<?xml version = "1.0" encoding = "utf-8" ?> 
<configuration> 
   
   <connectionStrings> 
      <add name = "default" connectionString = "Data Source =
         (localdb)\MSSQLLocalDB;Initial Catalog = NHibernateDemo;Integrated Security =
         True;Connect Timeout = 30;Encrypt = False;TrustServerCertificate = False;
         ApplicationIntent = ReadWrite;MultiSubnetFailover = False"/> 
   </connectionStrings> 

</configuration>

Чтобы установить NHibernate в свое приложение, выполните следующую команду в окне консоли NuGet Manager.

install-package NHibernate

Чтобы настроить конфигурацию NHibernate, нам нужно определить конфигурацию в hibernate.cfg.xml файл, как показано в следующем коде.

<xml version = "1.0" encoding = "utf-8" ?> 
<hibernate-configuration xmlns = "urn:nhibernate-configuration-2.2"> 
   
   <session-factory> 
      <property name = "connection.connection_string_name">default</property>
		
      <property name = "connection.driver_class">
         NHibernate.Driver.SqlClientDriver 
      </property> 
		
      <property name = "dialect">
         NHibernate.Dialect.MsSql2008Dialect
      </property> 
		
      <property name = "show_sql">true</property> 
   </session-factory> 

</hibernate-configuration>

В этом примере мы будем работать с двумя доменными классами: Customer и Order.

Вот реализация файла Customer.cs, в котором у нас есть два класса: один - это класс Customer, а другой - класс Location, в котором объект используется в качестве адреса в классе Customer.

using System; 
using System.Text; 
using Iesi.Collections.Generic;

namespace NHibernateDemo { 

   public class Customer { 
      
      public Customer() { 
         MemberSince = DateTime.UtcNow; 
         Orders = new HashedSet<Order>(); 
      } 
      
      public virtual Guid Id { get; set; } 
      public virtual string FirstName { get; set; } 
      public virtual string LastName { get; set; } 
      public virtual double AverageRating { get; set; } 
      public virtual int Points { get; set; } 
		
      public virtual bool HasGoldStatus { get; set; }
      public virtual DateTime MemberSince { get; set; } 
      public virtual CustomerCreditRating CreditRating { get; set; } 
      public virtual Location Address { get; set; }
		
      public virtual ISet<Order> Orders { get; set; }
      public virtual void AddOrder(Order order) { Orders.Add(order); order.Customer = this; }
      
      public override string ToString() { 
         var result = new StringBuilder();
			
         result.AppendFormat("{1} {2} ({0})\r\n\tPoints: {3}\r\n\tHasGoldStatus:
            {4}\r\n\tMemberSince: {5} ({7})\r\n\tCreditRating: {6}\r\n\tAverageRating:
            {8}\r\n", Id, FirstName, LastName, Points, HasGoldStatus, MemberSince,
            CreditRating, MemberSince.Kind, AverageRating);
            result.AppendLine("\tOrders:"); 
         
         foreach(var order in Orders) { 
            result.AppendLine("\t\t" + order); 
         } 
			
         return result.ToString(); 
      } 
   }
   
   public class Location { 
      public virtual string Street { get; set; } 
      public virtual string City { get; set; } 
      public virtual string Province { get; set; } 
      public virtual string Country { get; set; } 
   }
   
   public enum CustomerCreditRating { 
      Excellent,
      VeryVeryGood, 
      VeryGood, 
      Good, 
      Neutral, 
      Poor, 
      Terrible 
   } 
}

Вот файл сопоставления Customer.hbm.xml в котором класс Customer отображается в таблицу Customer.

<?xml version = "1.0" encoding = "utf-8" ?> 
<hibernate-mapping xmlns = "urn:nhibernate-mapping-2.2" assembly = "NHibernateDemo" 
   namespace = "NHibernateDemo"> 
	
   <class name = "Customer"> 
   
      <id name = "Id"> 
         <generator class = "guid.comb"/> 
      </id> 

      <property name = "FirstName"/> 
      <property name = "LastName"/> 
      <property name = "AverageRating"/> 
      <property name = "Points"/> 
      <property name = "HasGoldStatus"/> 
      <property name = "MemberSince" type = "UtcDateTime"/> 
      <property name = "CreditRating" type = "CustomerCreditRatingType"/>
      
      <component name = "Address"> 
         <property name = "Street"/> 
         <property name = "City"/> 
         <property name = "Province"/> 
         <property name = "Country"/> 
      </component> 

   </class> 
</hibernate-mapping>

У нас также есть класс заказа, и вот реализация Order.cs файл.

using System; using Iesi.Collections.Generic;

namespace NHibernateDemo { 

   public class Order { 
      
      public virtual Guid Id { get; set; } 
      public virtual DateTime Ordered { get; set; } 
      public virtual DateTime? Shipped { get; set; }
      public virtual Location ShipTo { get; set; } 
      public virtual Customer Customer { get; set; }
      
      public override string ToString() { 
         return string.Format("Order Id: {0}", Id); 
      } 
   } 
}

Отношения "многие к одному"

Нам также необходимо сопоставить класс Order с таблицей Order в базе данных, так что вот реализация Order.hbm.xml файл.

<?xml version = "1.0" encoding = "utf-8" ?> 
<hibernate-mapping xmlns = "urn:nhibernate-mapping-2.2" assembly = "NHibernateDemo" 
   namespace = "NHibernateDemo"> 

   <class name = "Order" table = "`Order`"> 
   
      <id name = "Id"> 
         <generator class = "guid.comb"/> 
      </id> 
		
      <property name = "Ordered"/> 
      <property name = "Shipped"/> 
   
      <component name = "ShipTo"> 
         <property name = "Street"/> 
         <property name = "City"/> 
         <property name = "Province"/> 
         <property name = "Country"/> 
      </component> 

      <!--<many-to-one name = "Customer" column = "CustomerId" cascade =
         "save-update"/>--> 
			
   </class> 
</hibernate-mapping>

Отношения один ко многим

Здесь мы собираемся взглянуть на отношения «один ко многим», в данном случае, между клиентом и заказами. У нас есть наш клиент, мы создаем нового, и вы можете видеть, что коллекция инициализируется следующей парой заказов.

private static Customer CreateCustomer() { 
   
   var customer = new Customer { 
      FirstName = "John", 
      LastName = "Doe", 
      Points = 100, 
      HasGoldStatus = true, 
      MemberSince = new DateTime(2012, 1, 1), 
      CreditRating = CustomerCreditRating.Good, 
      AverageRating = 42.42424242, 
      Address = CreateLocation() 
   }; 
   
   var order1 = new Order { 
      Ordered = DateTime.Now 
   }; 
   
   customer.AddOrder(order1); 
   var order2 = new Order { 
      Ordered = DateTime.Now.AddDays(-1), 
      Shipped = DateTime.Now, 
      ShipTo = CreateLocation() 
   }; 
   
   customer.AddOrder(order2); 
   return customer; 
}

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

private static void Main() { 

   var cfg = ConfigureNHibernate(); 
   var sessionFactory = cfg.BuildSessionFactory();
   
   Guid id; 
   using(var session = sessionFactory.OpenSession()) 
	
   using(var tx = session.BeginTransaction()) { 
      var newCustomer = CreateCustomer(); 
      Console.WriteLine("New Customer:"); 
      Console.WriteLine(newCustomer);
      session.Save(newCustomer); 
      id = newCustomer.Id; 
      tx.Commit(); 
   }
   
   using(var session = sessionFactory.OpenSession()) 
	
   using(var tx = session.BeginTransaction()) { 
      var reloaded = session.Load<Customer>(id); 
      Console.WriteLine("Reloaded:"); 
      Console.WriteLine(reloaded); 
      tx.Commit(); 
   }
   
   Console.WriteLine("Press <ENTER> to exit..."); 
   Console.ReadLine(); 
}

Вот полный Program.cs реализация файла.

using System; 
using System.Data; 
using System.Linq; 
using System.Reflection; 

using HibernatingRhinos.Profiler.Appender.NHibernate; 
using NHibernate.Cfg; 
using NHibernate.Dialect; 
using NHibernate.Driver; 
using NHibernate.Linq;

namespace NHibernateDemo {
 
   internal class Program { 
	
      private static void Main() { 
		
         var cfg = ConfigureNHibernate(); 
         var sessionFactory = cfg.BuildSessionFactory();
         
         Guid id; 
         using(var session = sessionFactory.OpenSession())
			
         using(var tx = session.BeginTransaction()) { 
            var newCustomer = CreateCustomer(); 
            Console.WriteLine("New Customer:"); 
            Console.WriteLine(newCustomer); 
            session.Save(newCustomer); 
            id = newCustomer.Id; 
            tx.Commit(); 
         } 
         
         using(var session = sessionFactory.OpenSession()) 
			
         using(var tx = session.BeginTransaction()) { 
            var reloaded = session.Load<Customer>(id); 
            Console.WriteLine("Reloaded:"); 
            Console.WriteLine(reloaded); 
            tx.Commit();
         }
         
         Console.WriteLine("Press <ENTER> to exit..."); 
         Console.ReadLine();
      }
      
      private static Customer CreateCustomer() { 
		
         var customer = new Customer { 
            FirstName = "John", 
            LastName = "Doe", 
            Points = 100, 
            HasGoldStatus = true, 
            MemberSince = new DateTime(2012, 1, 1), 
            CreditRating = CustomerCreditRating.Good, 
            AverageRating = 42.42424242, 
            Address = CreateLocation() 
         }; 
         
         var order1 = new Order { 
            Ordered = DateTime.Now
         }; 
         
         customer.AddOrder(order1); 
			
         var order2 = new Order { 
            Ordered = DateTime.Now.AddDays(-1), 
            Shipped = DateTime.Now, 
            ShipTo = CreateLocation() 
         }; 
         
         customer.AddOrder(order2); 
         return customer; 
      } 
      
      private static Location CreateLocation() { 
		
         return new Location { 
            Street = "123 Somewhere Avenue", 
            City = "Nowhere", 
            Province = "Alberta", 
            Country = "Canada" 
         }; 
      } 
      
      private static Configuration ConfigureNHibernate() { 
         NHibernateProfiler.Initialize(); 
         var cfg = new Configuration(); 
         
         cfg.DataBaseIntegration(x =&ht; { 
            x.ConnectionStringName = "default"; 
            x.Driver<SqlClientDriver>(); 
            x.Dialect<MsSql2008Dialect>(); 
            x.IsolationLevel = IsolationLevel.RepeatableRead; 
            x.Timeout = 10; x.BatchSize = 10; 
         }); 
         
         cfg.SessionFactory().GenerateStatistics();
         cfg.AddAssembly(Assembly.GetExecutingAssembly()); 
         return cfg; 
      } 
   } 
}

Когда вы запустите это приложение, вы увидите следующий результат.

New Customer:

John Doe (00000000-0000-0000-0000-000000000000)
   Points: 100
   HasGoldStatus: True
   MemberSince: 1/1/2012 12:00:00 AM (Unspecified)
   CreditRating: Good
   AverageRating: 42.42424242

   Orders:
      Order Id: 00000000-0000-0000-0000-000000000000
      Order Id: 00000000-0000-0000-0000-000000000000

Reloaded:
John Doe (9b0fcf10-83f6-4f39-bda5-a5b800ede2ba)
   Points: 100
   HasGoldStatus: True
   MemberSince: 1/1/2012 12:00:00 AM (Utc)
   CreditRating: Good
   AverageRating: 42.4242

   Orders:
Press <ENTER> to exit...

Как видите, изначально у покупателя 2 заказа, но когда мы его перезагружаем, заказов не видно. Если вы посмотрите наcustomer.hbm.xmlфайл, вы можете увидеть здесь, что мы не отображаем фактический сбор заказов. Итак, NHibernate ничего об этом не знает. Давайте продолжим и добавим это.

<?xml version = "1.0" encoding = "utf-8" ?> 
<hibernate-mapping xmlns = "urn:nhibernate-mapping-2.2" 
   assembly = "NHibernateDemo" namespace = "NHibernateDemo">
	
   <class name = "Customer"> 
      
      <id name = "Id">
         <generator class = "guid.comb"/> 
      </id> 
      
      <property name = "FirstName"/>
      <property name = "LastName"/> 
      <property name = "AverageRating"/> 
      <property name = "Points"/> 
      <property name = "HasGoldStatus"/> 
      <property name = "MemberSince" type = "UtcDateTime"/> 
      <property name = "CreditRating" type = "CustomerCreditRatingType"/>
      
      <component name = "Address"> 
         <property name = "Street"/> 
         <property name = "City"/> 
         <property name = "Province"/> 
         <property name = "Country"/> 
      </component>
      
      <set name = "Orders" table = "`Order`"> 
         <key column = "CustomerId"/> 
         <one-to-many class = "Order"/> 
      </set> 
   
   </class> 
</hibernate-mapping>

Это набор, имя которого - «Заказы», ​​которое хранится в таблице под названием порядок. Нам нужно указать ключ, который является именем внешнего ключа или для поиска заказов. Эти заказы идентифицируются или принадлежат покупателю по его идентификатору. И затем я должен отметить, что это отношение «один ко многим», и это связано с классом порядка.

Нам также необходимо немного изменить метод Main, сохранив новые заказы клиентов в базе данных, как показано в следующей программе.

private static void Main() { 

   var cfg = ConfigureNHibernate(); 
   var sessionFactory = cfg.BuildSessionFactory();
   
   Guid id; 
   using(var session = sessionFactory.OpenSession()) 
	
   using(var tx = session.BeginTransaction()) {
      var newCustomer = CreateCustomer(); 
      Console.WriteLine("New Customer:"); 
      Console.WriteLine(newCustomer);
      session.Save(newCustomer); 
      
      foreach (var order in newCustomer.Orders) { 
         session.Save(order); 
      } 
		
      id = newCustomer.Id; 
      tx.Commit(); 
   }
   
   using(var session = sessionFactory.OpenSession()) 
	
   using(var tx = session.BeginTransaction()) { 
      var reloaded = session.Load<Customer>(id); 
      Console.WriteLine("The orders were ordered by: "); 
      
      foreach (var order in reloaded.Orders) { 
         Console.WriteLine(order.Customer); 
      } 
		
      tx.Commit(); 
   }
   Console.WriteLine("Press <ENTER> to exit..."); Console.ReadLine();
}

Мы также указали, какой клиент заказал этот конкретный продукт. Поэтому нам нужно создать отношения «многие к одному», чтобы связать этот заказ с этим клиентом.

Итак, давайте перейдем к Order.hbm.xml файл и добавьте многие к одному, а затем назовите поле клиента и столбец с идентификатором клиента.

<?xml version = "1.0" encoding = "utf-8" ?> 
<hibernate-mapping xmlns = "urn:nhibernate-mapping-2.2" assembly = "NHibernateDemo"
   namespace = "NHibernateDemo"> 
	
   <class name = "Order" table = "`Order`">
      
      <id name = "Id"> 
         <generator class = "guid.comb"/> 
      </id>
      
      <property name = "Ordered"/> 
      <property name = "Shipped"/> 
      
      <component name = "ShipTo"> 
         <property name = "Street"/> 
         <property name = "City"/> 
         <property name = "Province"/> 
         <property name = "Country"/> 
      </component> 

      <many-to-one name = "Customer" column = "CustomerId"/> 
		
   </class> 
</hibernate-mapping>

Давайте снова запустим это приложение, и теперь вы увидите следующий результат.

New Customer:
John Doe (00000000-0000-0000-0000-000000000000)
   Points: 100
   HasGoldStatus: True
   MemberSince: 1/1/2012 12:00:00 AM (Unspecified)
   CreditRating: Good
   AverageRating: 42.42424242

   Orders:
      Order Id: 00000000-0000-0000-0000-000000000000
      Order Id: 00000000-0000-0000-0000-000000000000

Reloaded:
John Doe (660a6f29-650e-4380-99e0-a5b800febbde)
   Points: 100
   HasGoldStatus: True
   MemberSince: 1/1/2012 12:00:00 AM (Utc)
   CreditRating: Good
   AverageRating: 42.4242

   Orders:
      Order Id: 57314deb-e023-4e55-ac1e-a5b800febbe3
      Order Id: fc065683-d5f5-484b-ae42-a5b800febbe3

The orders were ordered by:
John Doe (660a6f29-650e-4380-99e0-a5b800febbde)
   Points: 100
   HasGoldStatus: True
   MemberSince: 1/1/2012 12:00:00 AM (Utc)
   CreditRating: Good
   AverageRating: 42.4242

   Orders:
      Order Id: 57314deb-e023-4e55-ac1e-a5b800febbe3
      Order Id: fc065683-d5f5-484b-ae42-a5b800febbe3

John Doe (660a6f29-650e-4380-99e0-a5b800febbde)
   Points: 100
   HasGoldStatus: True
   MemberSince: 1/1/2012 12:00:00 AM (Utc)
   CreditRating: Good
   AverageRating: 42.4242

   Orders:
      Order Id: 57314deb-e023-4e55-ac1e-a5b800febbe3
      Order Id: fc065683-d5f5-484b-ae42-a5b800febbe3
		
Press <ENTER> to exit...

В этой главе мы рассмотрим, как представлять коллекции. Существуют разные типы коллекций, которые мы можем использовать в NHibernate, такие как -

  • Lists
  • Sets
  • Bags

Теперь, с точки зрения .NET, мы обычно имеем дело со списками или подобными очень простыми структурами данных, списками, словарями. .NET не имеет большого разнообразия различных типов коллекций. Так зачем же NHibernate все эти разные типы? Это действительно возвращается в базу данных.

Список

  • Список - это упорядоченный набор элементов, которые не обязательно уникальны.

  • Мы можем отобразить это, используя IList <T>.

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

Набор

  • Набор - это неупорядоченный набор уникальных элементов. Если вы попытаетесь вставить 2 повторяющихся элемента в набор, это вызовет исключение.

  • В NHibernate нет ничего особенного.

  • Это просто удобный способ реализации общего набора. Если вы используете .NET 4, вы можете использовать новыйHashSet <T> для их представления, но в большинстве приложений NHibernate мы представляем, что это ISet.

  • Он неупорядоченный: если вы извлечете список адресов из базы данных или список заказов, вы не узнаете, в каком порядке они поступают, если не введете конкретное предложение Order by.

  • В общем, данные, которые вы извлекаете из базы данных, являются наборами.

  • Это уникальные неупорядоченные коллекции элементов.

Сумка

  • Еще одна распространенная коллекция, которую мы увидим в мире баз данных, - это сумка, которая похожа на набор, но может иметь повторяющиеся элементы.

  • В мире .NET мы представляем это с помощью IList.

Наборы, вероятно, самые распространенные, но вы также увидите списки и пакеты в зависимости от вашего приложения. Давайте посмотрим на нижеприведенныйcustomer.hbm.xml файл из последней главы, в которой определены сетевые заказы.

<?xml version = "1.0" encoding = "utf-8" ?> 
<hibernate-mapping xmlns = "urn:nhibernate-mapping-2.2" assembly = "NHibernateDemo" 
   namespace = "NHibernateDemo"> 
	
   <class name = "Customer"> 
      
      <id name = "Id"> 
         <generator class = "guid.comb"/> 
      </id> 
   
      <property name = "FirstName"/> 
      <property name = "LastName"/> 
      <property name = "AverageRating"/> 
      <property name = "Points"/> 
      <property name = "HasGoldStatus"/> 
      <property name = "MemberSince" type = "UtcDateTime"/> 
      <property name = "CreditRating" type = "CustomerCreditRatingType"/>
      
      <component name = "Address"> 
         <property name = "Street"/> 
         <property name = "City"/> 
         <property name = "Province"/> 
         <property name = "Country"/> 
      </component>
      
      <set name = "Orders" table = "`Order`"> 
         <key column = "CustomerId"/> 
         <one-to-many class = "Order"/> 
      </set> 
   
   </class> 
</hibernate-mapping>

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

Теперь, если вы посмотрите на класс Customer, вы увидите, что свойство Orders определяется с помощью ISet, как показано в следующей программе.

public virtual ISet<Order> Orders { get; set; }

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

New Customer:
John Doe (00000000-0000-0000-0000-000000000000)
   Points: 100
   HasGoldStatus: True
   MemberSince: 1/1/2012 12:00:00 AM (Unspecified)
   CreditRating: Good
   AverageRating: 42.42424242

   Orders:
      Order Id: 00000000-0000-0000-0000-000000000000
      Order Id: 00000000-0000-0000-0000-000000000000

Reloaded:
John Doe (1f248133-b50a-4ad7-9915-a5b8017d0ff1)
   Points: 100
   HasGoldStatus: True
   MemberSince: 1/1/2012 12:00:00 AM (Utc)
   CreditRating: Good
   AverageRating: 42.4242

   Orders:
      Order Id: c41af8f2-7124-42a7-91c5-a5b8017d0ff6
      Order Id: 657f6bb0-1f42-45fc-8fc7-a5b8017d0ff7

The orders were ordered by:
John Doe (1f248133-b50a-4ad7-9915-a5b8017d0ff1)
   Points: 100
   HasGoldStatus: True
   MemberSince: 1/1/2012 12:00:00 AM (Utc)
   CreditRating: Good
   AverageRating: 42.4242

   Orders:
      Order Id: c41af8f2-7124-42a7-91c5-a5b8017d0ff6
      Order Id: 657f6bb0-1f42-45fc-8fc7-a5b8017d0ff7

John Doe (1f248133-b50a-4ad7-9915-a5b8017d0ff1)
   Points: 100
   HasGoldStatus: True
   MemberSince: 1/1/2012 12:00:00 AM (Utc)
   CreditRating: Good
   AverageRating: 42.4242

   Orders:
      Order Id: c41af8f2-7124-42a7-91c5-a5b8017d0ff6
      Order Id: 657f6bb0-1f42-45fc-8fc7-a5b8017d0ff7
		
Press <ENTER> to exit...

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

<bag name = "Orders" table = "`Order`"> 
   <key column = "CustomerId"/> 
   <one-to-many class = "Order"/> 
</bag>

Теперь, если вы запустите это приложение, вы получите исключение, потому что, если мы посмотрим на класс клиента, вы заметите, что заказы помечены как ISet в коде C #.

Таким образом, нам также нужно будет изменить это на IList, а затем здесь нам нужно будет перейти с HashSet на List в конструкторе.

public class Customer { 
   public Customer() { 
      MemberSince = DateTime.UtcNow; 
      Orders = new List<Order>(); 
   } 
	
   public virtual Guid Id { get; set; } 
   public virtual string FirstName { get; set; } 
   public virtual string LastName { get; set; } 
   public virtual double AverageRating { get; set; } 
   public virtual int Points { get; set; } 
	
   public virtual bool HasGoldStatus { get; set; } 
   public virtual DateTime MemberSince { get; set; } 
   public virtual CustomerCreditRating CreditRating { get; set; } 
   public virtual Location Address { get; set; }
   public virtual IList<Order> Orders { get; set; }
   public virtual void AddOrder(Order order) { Orders.Add(order); order.Customer = this; }
   
   public override string ToString() { 
      var result = new StringBuilder(); 
		
      result.AppendFormat("{1} {2} ({0})\r\n\tPoints: {3}\r\n\tHasGoldStatus:
         {4}\r\n\tMemberSince: {5} ({7})\r\n\tCreditRating: {6}\r\n\tAverageRating:
         {8}\r\n", Id, FirstName, LastName, Points, HasGoldStatus, MemberSince,
         CreditRating, MemberSince.Kind, AverageRating); result.AppendLine("\tOrders:"); 
      
      foreach(var order in Orders) { 
         result.AppendLine("\t\t" + order); 
      } 
		
      return result.ToString(); 
   } 
}

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

John Doe (00000000-0000-0000-0000-000000000000)
   Points: 100
   HasGoldStatus: True
   MemberSince: 1/1/2012 12:00:00 AM (Unspecified)
   CreditRating: Good
   AverageRating: 42.42424242

   Orders:
      Order Id: 00000000-0000-0000-0000-000000000000
      Order Id: 00000000-0000-0000-0000-000000000000

Reloaded:
John Doe (fbde48f5-d620-4d1c-9a7f-a5b8017c3280)
   Points: 100
   HasGoldStatus: True
   MemberSince: 1/1/2012 12:00:00 AM (Utc)
   CreditRating: Good
   AverageRating: 42.4242

   Orders:
      Order Id: 6dd7dbdb-354f-4c82-9c39-a5b8017c3286
      Order Id: 9b3e2441-a81b-404d-9aed-a5b8017c3287

The orders were ordered by:
John Doe (fbde48f5-d620-4d1c-9a7f-a5b8017c3280)
   Points: 100
   HasGoldStatus: True
   MemberSince: 1/1/2012 12:00:00 AM (Utc)
   CreditRating: Good
   AverageRating: 42.4242

   Orders:
      Order Id: 6dd7dbdb-354f-4c82-9c39-a5b8017c3286
      Order Id: 9b3e2441-a81b-404d-9aed-a5b8017c3287

John Doe (fbde48f5-d620-4d1c-9a7f-a5b8017c3280)
   Points: 100
   HasGoldStatus: True
   MemberSince: 1/1/2012 12:00:00 AM (Utc)
   CreditRating: Good
   AverageRating: 42.4242

   Orders:
      Order Id: 6dd7dbdb-354f-4c82-9c39-a5b8017c3286
      Order Id: 9b3e2441-a81b-404d-9aed-a5b8017c3287
		
Press <ENTER> to exit...

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

  • Мы также могли нарушить ограничения внешнего ключа, поэтому мы можем использовать понятие каскадов.

  • По умолчанию NHibernate не выполняет каскадные операции с дочерними объектами.

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

  • Таким образом, вы не захотите обязательно каскадировать эти отношения, потому что другие клиенты все еще ссылаются на них.

  • Таким образом, вся идея каскадов заключается в том, чтобы сообщить NHibernate, как обращаться с дочерними объектами.

Существуют различные варианты каскадирования, а именно:

  • none - это значение по умолчанию, которое означает отсутствие каскадирования.

  • all - который будет каскадно сохранять, обновлять и удалять.

  • save-update - он будет каскадно сохраняться и обновляться.

  • delete - будет каскадное удаление.

  • all-delete-orphan - это специальный, который довольно часто используется и аналогичен All Except, если он находит строки Delete-orphan, он их также удаляет.

Вы можете указать значение по умолчанию в своем hbm.xml файл, поэтому вы можете предоставить каскад по умолчанию для этого элемента сопоставления Hibernate или указать его для конкретных коллекций и отношений, таких как многие-к-одному.

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

using(var session = sessionFactory.OpenSession()) 

using(var tx = session.BeginTransaction()) { 
   var newCustomer = CreateCustomer(); 
   Console.WriteLine("New Customer:"); 
   Console.WriteLine(newCustomer); 
   session.Save(newCustomer); 
	
   foreach (var order in newCustomer.Orders) { 
      session.Save(order); 
   } 
	
   id = newCustomer.Id; 
   tx.Commit(); 
}

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

using(var session = sessionFactory.OpenSession())
 
using(var tx = session.BeginTransaction()) { 
   var newCustomer = CreateCustomer(); 
   Console.WriteLine("New Customer:"); 
   Console.WriteLine(newCustomer);
	
   session.Save(newCustomer); 
   id = newCustomer.Id; 
   tx.Commit(); 
}

Нам нужно указать опцию каскада в customer.hbm.xml.

<?xml version = "1.0" encoding = "utf-8" ?> 
<hibernate-mapping xmlns = "urn:nhibernate-mapping-2.2" assembly = "NHibernateDemo"
   namespace = "NHibernateDemo"> 
	
   <class name = "Customer"> 
   
      <id name = "Id"> 
         <generator class = "guid.comb"/> 
      </id> 
      
      <property name = "FirstName"/> 
      <property name = "LastName"/> 
      <property name = "AverageRating"/> 
      <property name = "Points"/> 
      <property name = "HasGoldStatus"/> 
      <property name = "MemberSince" type = "UtcDateTime"/> 
      <property name = "CreditRating" type = "CustomerCreditRatingType"/>
      
      <component name = "Address"> 
         <property name = "Street"/> 
         <property name = "City"/> 
         <property name = "Province"/> 
         <property name = "Country"/> 
      </component>
      
      <set name = "Orders" table = "`Order`" cascade = "all-delete-orphan"> 
         <key column = "CustomerId"/> 
         <one-to-many class = "Order"/> 
      </set> 
   
   </class> 
</hibernate-mapping>
  • Теперь заказы полностью принадлежат покупателю. Таким образом, если клиенты были удалены из базы данных, наше приложение здесь захочет удалить все эти заказы, включая те, которые могли быть потеряны.

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

  • Таким образом, вы действительно можете каскадировать эти удаления. Так что сAll, он будет сохранять, обновлять и удалять.

Теперь, когда вы запустите это приложение, вы увидите следующий результат.

New Customer:
John Doe (00000000-0000-0000-0000-000000000000)
   Points: 100
   HasGoldStatus: True
   MemberSince: 1/1/2012 12:00:00 AM (Unspecified)
   CreditRating: Good
   AverageRating: 42.42424242

   Orders:
      Order Id: 00000000-0000-0000-0000-000000000000
      Order Id: 00000000-0000-0000-0000-000000000000

Reloaded:
John Doe (10b2a3d7-7fcf-483c-b1da-a5bb00b8512e)
   Points: 100
   HasGoldStatus: True
   MemberSince: 1/1/2012 12:00:00 AM (Utc)
   CreditRating: Good
   AverageRating: 42.4242

   Orders:
      Order Id: e6680e30-5b3b-4efa-b017-a5bb00b85133
      Order Id: b03858e7-8c36-4555-8878-a5bb00b85134

The orders were ordered by:
John Doe (10b2a3d7-7fcf-483c-b1da-a5bb00b8512e)
   Points: 100
   HasGoldStatus: True
   MemberSince: 1/1/2012 12:00:00 AM (Utc)
   CreditRating: Good
   AverageRating: 42.4242

   Orders:
      Order Id: e6680e30-5b3b-4efa-b017-a5bb00b85133
      Order Id: b03858e7-8c36-4555-8878-a5bb00b85134

John Doe (10b2a3d7-7fcf-483c-b1da-a5bb00b8512e)
   Points: 100
   HasGoldStatus: True
   MemberSince: 1/1/2012 12:00:00 AM (Utc)
   CreditRating: Good
   AverageRating: 42.4242

   Orders:
      Order Id: e6680e30-5b3b-4efa-b017-a5bb00b85133
      Order Id: b03858e7-8c36-4555-8878-a5bb00b85134
		
Press <ENTER> to exit...

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

Так что, в зависимости от ваших отношений, вы можете захотеть их каскадировать. Теперь давайте посмотрим на другое каскадное отношение. Давай пойдем вOrder.hbm.xml файл, и мы можем каскадировать отношения "многие к одному".

<?xml version = "1.0" encoding = "utf-8" ?> 
<hibernate-mapping xmlns = "urn:nhibernate-mapping-2.2" assembly = "NHibernateDemo"
   namespace = "NHibernateDemo"> 
   
   <class name = "Order" table = "`Order`"> 
	
      <id name = "Id"> 
         <generator class = "guid.comb"/> 
      </id> 

      <property name = "Ordered"/> 
      <property name = "Shipped"/> 
      
      <component name = "ShipTo"> 
         <property name = "Street"/> 
         <property name = "City"/> 
         <property name = "Province"/> 
         <property name = "Country"/> 
      </component> 
      
      <many-to-one name = "Customer" column = "CustomerId" cascade = "save-update"/>
		
   </class> 
</hibernate-mapping>

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

Итак, здесь мы хотели бы сделать обновление сохранения, поэтому, используя save-update, он будет каскадировать любые сохранения или обновления для этого клиента. Итак, если у нас появляется новый клиент или мы меняем клиента, это будет каскадно. Если это удаление, оно не удалит его из базы данных.

Итак, запустив наше приложение снова, все по-прежнему работает, как ожидалось.

New Customer:
John Doe (00000000-0000-0000-0000-000000000000)
   Points: 100
   HasGoldStatus: True
   MemberSince: 1/1/2012 12:00:00 AM (Unspecified)
   CreditRating: Good
   AverageRating: 42.42424242

   Orders:
      Id: 00000000-0000-0000-0000-000000000000
      Order Id: 00000000-0000-0000-0000-000000000000

Reloaded:
John Doe (10b2a3d7-7fcf-483c-b1da-a5bb00b8512e)
   Points: 100
   HasGoldStatus: True
   MemberSince: 1/1/2012 12:00:00 AM (Utc)
   CreditRating: Good
   AverageRating: 42.4242

   Orders:
      Order Id: e6680e30-5b3b-4efa-b017-a5bb00b85133
      Order Id: b03858e7-8c36-4555-8878-a5bb00b85134

The orders were ordered by:
John Doe (10b2a3d7-7fcf-483c-b1da-a5bb00b8512e)
   Points: 100
   HasGoldStatus: True
   MemberSince: 1/1/2012 12:00:00 AM (Utc)
   CreditRating: Good
   AverageRating: 42.4242

   Orders:
      Order Id: e6680e30-5b3b-4efa-b017-a5bb00b85133
      Order Id: b03858e7-8c36-4555-8878-a5bb00b85134
      John Doe (10b2a3d7-7fcf-483c-b1da-a5bb00b8512e)
		
   Points: 100
   HasGoldStatus: True
   MemberSince: 1/1/2012 12:00:00 AM (Utc)
   CreditRating: Good
   AverageRating: 42.4242

   Orders:
      Order Id: e6680e30-5b3b-4efa-b017-a5bb00b85133
      Order Id: b03858e7-8c36-4555-8878-a5bb00b85134
		
Press <ENTER> to exit...

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

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

  • Коллекция заказов будет загружена по запросу.

  • Любая ассоциация, будь то многие-к-одному или коллекция, загружается лениво по умолчанию, для нее требуется Open ISession.

  • Если вы закрыли сеанс или зафиксировали транзакцию, вы можете получить исключение ленивой загрузки, которое не может извлечь эти дополнительные объекты.

  • Вы должны быть осторожны с отложенной загрузкой и тем, сколько данных вам действительно нужно.

  • Вы можете отключить отложенную загрузку для всей ассоциации или поставить lazy равным false, или вы также можете указать стратегию выборки.

Здесь Program.cs реализация файла.

using System; 
using System.Data; 
using System.Linq; 
using System.Reflection; 

using HibernatingRhinos.Profiler.Appender.NHibernate; 
using NHibernate.Cfg; 
using NHibernate.Dialect; 
using NHibernate.Driver; 
using NHibernate.Linq;

namespace NHibernateDemo { 

   internal class Program { 
	
      private static void Main() { 
		
         var cfg = ConfigureNHibernate(); 
         var sessionFactory = cfg.BuildSessionFactory();
         
         Guid id; 
         using(var session = sessionFactory.OpenSession()) 
			
         using(var tx = session.BeginTransaction()) {
            var newCustomer = CreateCustomer(); 
            Console.WriteLine("New Customer:"); 
            Console.WriteLine(newCustomer); 
            session.Save(newCustomer); 
            id = newCustomer.Id; 
            tx.Commit(); 
         }
         
         using(var session = sessionFactory.OpenSession()) 
			
         using(var tx = session.BeginTransaction()) { 
            var reloaded = session.Load<Customer>(id); 
            Console.WriteLine("Reloaded:"); 
            Console.WriteLine(reloaded); 
            Console.WriteLine("The orders were ordered by: "); 
            
            foreach (var order in reloaded.Orders) { 
               Console.WriteLine(order.Customer); 
            } 
				
            tx.Commit(); 
         }
			
         Console.WriteLine("Press <ENTER> to exit..."); 
         Console.ReadLine(); 
      }
		
      private static Customer CreateCustomer() { 
         
         var customer = new Customer { 
            FirstName = "John", 
            LastName = "Doe", 
            Points =100, 
            HasGoldStatus = true, 
            MemberSince = new DateTime(2012, 1, 1),
            CreditRating = CustomerCreditRating.Good,
            AverageRating = 42.42424242, 
            Address = CreateLocation() 
         }; 
			
         var order1 = new Order { Ordered = DateTime.Now }; 
         customer.AddOrder(order1); 
         
         var order2 = new Order { 
            Ordered = DateTime.Now.AddDays(-1), 
            Shipped = DateTime.Now, 
            ShipTo = CreateLocation() 
         }; 
			
         customer.AddOrder(order2); return customer; 
      }
		
      private static Location CreateLocation() { 
         return new Location { 
            Street = "123 Somewhere Avenue", 
            City = "Nowhere", 
            Province = "Alberta", 
            Country = "Canada" 
         }; 
      }
		
      private static Configuration ConfigureNHibernate() { 
		
         NHibernateProfiler.Initialize(); 
         var cfg = new Configuration(); 
         
         cfg.DataBaseIntegration(x => { 
            x.ConnectionStringName = "default"; 
            x.Driver<SqlClientDriver>(); 
            x.Dialect<MsSql2008Dialect<(); 
            x.IsolationLevel = IsolationLevel.RepeatableRead; 
            x.Timeout = 10;
            x.BatchSize = 10;
         }); 
         
         cfg.SessionFactory().GenerateStatistics();
         cfg.AddAssembly(Assembly.GetExecutingAssembly()); 
         return cfg; 
      } 
   } 
}

Чтобы понять это, запустим приложение и взглянем на NHibernate Profiler.

Как вы можете видеть, у нас есть Select From Customer (Выбор из клиентов) с заданным идентификатором клиента, а также еще одна таблица Select From Orders, когда она фактически обращается к коллекции этого клиента.

Итак, у нас есть 2 обращения к базе данных. Иногда нам нужно это оптимизировать. Для этого перейдем кcustomer.hbm.xml файл, добавьте стратегию выборки и попросите его выполнить объединенную выборку.

<?xml version = "1.0" encoding = "utf-8" ?> 
<hibernate-mapping xmlns = "urn:nhibernate-mapping-2.2" assembly = "NHibernateDemo"
   namespace = "NHibernateDemo"> 
   
   <class name = "Customer"> 
	
      <id name = "Id"> 
         <generator class = "guid.comb"/> 
      </id> 
   
      <property name = "FirstName"/> 
      <property name = "LastName"/> 
      <property name = "AverageRating"/> 
      <property name = "Points"/> 
      <property name = "HasGoldStatus"/> 
      <property name = "MemberSince" type = "UtcDateTime"/> 
      <property name = "CreditRating" type = "CustomerCreditRatingType"/>
      
      <component name = "Address"> 
         <property name = "Street"/> 
         <property name = "City"/> 
         <property name = "Province"/> 
         <property name = "Country"/> 
      </component>
      
      <set name = "Orders" table = "`Order`" cascade = "all-delete-orphan" 
         fetch = "join"> 
         <key column = "CustomerId"/> 
         <one-to-many class = "Order"/> 
      </set> 
   
   </class> 
</hibernate-mapping>

Как видите, мы не изменили код в нашем приложении, мы только что добавили стратегию выборки в customer.hbm.xml. Давайте снова запустим это приложение, оно по-прежнему ведет себя точно так же. Давайте посмотрим на NHibernate Profiler.

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

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

  • Мы сохранили 1 поездку туда и обратно в базу данных.

  • Обратной стороной является то, что информация о клиенте будет дублироваться в обеих строках, и именно так работает левое внешнее соединение SQL.

  • Таким образом, со стратегией выборки мы извлекаем немного больше данных и сохраняем обратный путь.

Вы также можете сделать это на уровне запроса, поэтому перейдем к Program.cs файл и посмотрите на более простой пример перезагрузки.

using(var session = sessionFactory.OpenSession()) 

using(var tx = session.BeginTransaction()) { 
   //var query = from customer in session.Query<Customer>() 
   // select customer; 
   //var reloaded = query.Fetch(x => x.Orders).ToList();
	
   var reloaded = session.Load<Customer>(id); 
   Console.WriteLine("Reloaded:"); 
   Console.WriteLine(reloaded); 
   Console.WriteLine("The orders were ordered by: "); 
   
   foreach (var order in reloaded.Orders) { 
      Console.WriteLine(order.Customer); 
   } 
	
   tx.Commit(); 
}

Здесь мы делаем нагрузку по заказу. Теперь давайте изменим его на запрос, и мы будем использовать запрос ссылки, как показано в следующем коде.

using(var session = sessionFactory.OpenSession()) 

using(var tx = session.BeginTransaction()) {
   var query = from customer in session.Query<Customer>() 
   where customer.Id == id select customer; 
   var reloaded = query.Fetch(x => x.Orders).ToList().First();
	
   Console.WriteLine("Reloaded:"); 
   Console.WriteLine(reloaded); 
	
   tx.Commit();
}

Давайте также удалим стратегию выборки из customer.hbm.xml файл.

<?xml version = "1.0" encoding = "utf-8" ?> 
<hibernate-mapping xmlns = "urn:nhibernate-mapping-2.2" assembly = "NHibernateDemo"
   namespace = "NHibernateDemo"> 

   <class name = "Customer"> 
	
      <id name = "Id"> 
         <generator class = "guid.comb"/> 
      </id> 
   
      <property name = "FirstName"/> 
      <property name = "LastName"/>
      <property name = "AverageRating"/> 
      <property name = "Points"/> 
      <property name = "HasGoldStatus"/> 
      <property name = "MemberSince" type = "UtcDateTime"/> 
      <property name = "CreditRating" type = "CustomerCreditRatingType"/>
   
      <component name = "Address"> 
         <property name = "Street"/> 
         <property name = "City"/> 
         <property name = "Province"/> 
         <property name = "Country"/> 
      </component>
   
      <set name = "Orders" table = "`Order`" cascade = "all-delete-orphan"> 
         <key column = "CustomerId"/> 
         <one-to-many class = "Order"/> 
      </set> 
   
   </class> 
</hibernate-mapping>

Давайте снова запустим это приложение, и вы увидите следующий результат.

New Customer:
John Doe (00000000-0000-0000-0000-000000000000)
   Points: 100
   HasGoldStatus: True
   MemberSince: 1/1/2012 12:00:00 AM (Unspecified)
   CreditRating: Good
   AverageRating: 42.42424242

   Orders:
      Order Id: 00000000-0000-0000-0000-000000000000
      Order Id: 00000000-0000-0000-0000-000000000000

Reloaded:
John Doe (6ebacd17-f9ba-4ad8-9817-a5bb01112a5a)
   Points: 100
   HasGoldStatus: True
   MemberSince: 1/1/2012 12:00:00 AM (Utc)
   CreditRating: Good
   AverageRating: 42.4242

   Orders:
      Order Id: 16a6596b-d56e-41c7-9681-a5bb01112a60
      Order Id: d41d615b-0f21-4032-81db-a5bb01112a61
		
Press <ENTER> to exit...

Теперь давайте посмотрим на NHibernate Profiler, вы увидите, что у нас снова происходит активная загрузка соединения, но на этот раз она основана на запросе.

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

  • С реляционной точки зрения у вас есть один внешний ключ, и он представляет как клиента для заказа, так и заказы для клиента.

  • Из объектно-ориентированной модели у вас есть однонаправленные ассоциации с использованием этих ссылок.

  • Ничто не говорит о том, что две однонаправленные ассоциации представляют одну и ту же двунаправленную ассоциацию в базе данных.

  • Проблема здесь в том, что NHibernate не имеет достаточно информации, чтобы знать, что customer.orders и order.customer представляют такое же отношение в базе данных.

  • Нам нужно предоставить inverse equals true как подсказка, это потому, что однонаправленные ассоциации используют одни и те же данные.

  • Если мы попытаемся сохранить эти отношения, на которые есть 2 ссылки, NHibernate попытается обновить эту ссылку дважды.

  • Он фактически сделает дополнительный обход базы данных, а также будет иметь 2 обновления этого внешнего ключа.

  • Обратное равенство true указывает NHibernate, какую сторону отношения следует игнорировать.

  • Когда вы применяете его к коллекции, NHibernate всегда будет обновлять внешний ключ с другой стороны, со стороны дочернего объекта.

  • Тогда у нас есть только одно обновление этого внешнего ключа, и у нас нет дополнительных обновлений этих данных.

  • Это позволяет нам предотвратить эти повторяющиеся обновления внешнего ключа, а также помогает предотвратить нарушения внешнего ключа.

Давайте посмотрим на customer.cs файл, в котором вы увидите AddOrderметод, и идея здесь в том, что теперь у нас есть этот обратный указатель от заказа до клиента, и его нужно установить. Поэтому, когда заказ добавляется к клиенту, устанавливается обратный указатель этого клиента, в противном случае он был бы нулевым, поэтому нам нужно это, чтобы они правильно соединялись в графе объектов.

using System; 
using System.Text; 
using Iesi.Collections.Generic;

namespace NHibernateDemo {
 
   public class Customer { 
      
      public Customer() {
         MemberSince = DateTime.UtcNow; Orders = new HashedSet<Order>();
      } 
      
      public virtual Guid Id { get; set; } 
      public virtual string FirstName { get; set; } 
      public virtual string LastName { get; set; } 
      public virtual double AverageRating { get; set; } 
      public virtual int Points { get; set; } 
      public virtual bool HasGoldStatus { get; set; } 
		
      public virtual DateTime MemberSince { get; set; } 
      public virtual CustomerCreditRating CreditRating { get; set; } 
      public virtual Location Address { get; set; }
      public virtual ISet<Order> Orders { get; set; }
      public virtual void AddOrder(Order order) { Orders.Add(order); order.Customer = this; }
      
      public override string ToString() { 
         var result = new StringBuilder(); 
			
         result.AppendFormat("{1} {2} ({0})\r\n\tPoints: {3}\r\n\tHasGoldStatus:
            {4}\r\n\tMemberSince: {5} ({7})\r\n\tCreditRating: {6}\r\n\tAverageRating:
            {8}\r\n", Id, FirstName, LastName, Points, HasGoldStatus, MemberSince,
            CreditRating, MemberSince.Kind, AverageRating);
         result.AppendLine("\tOrders:"); 
         
         foreach(var order in Orders) { 
            result.AppendLine("\t\t" + order); 
         } 
			
         return result.ToString(); 
      } 
   }
   
   public class Location { 
      public virtual string Street { get; set; } 
      public virtual string City { get; set; } 
      public virtual string Province { get; set; } 
      public virtual string Country { get; set; }
   } 
   
   public enum CustomerCreditRating { 
      Excellent, 
      VeryVeryGood, 
      VeryGood, 
      Good, 
      Neutral, 
      Poor, 
      Terrible 
   } 
}

Здесь Program.cs реализация файла.

using System; 
using System.Data; 
using System.Linq; 
using System.Reflection; 

using HibernatingRhinos.Profiler.Appender.NHibernate; 
using NHibernate.Cfg; 
using NHibernate.Dialect; 
using NHibernate.Driver; 
using NHibernate.Linq;

namespace NHibernateDemo { 

   internal class Program { 
	
      private static void Main() { 
		
         var cfg = ConfigureNHibernate(); 
         var sessionFactory = cfg.BuildSessionFactory();
         Guid id; 
         using(var session = sessionFactory.OpenSession()) 
         
         using(var tx = session.BeginTransaction()) { 
            var newCustomer = CreateCustomer(); 
            Console.WriteLine("New Customer:"); 
            Console.WriteLine(newCustomer); 
            session.Save(newCustomer); 
            id = newCustomer.Id;
            tx.Commit(); 
         }
         
         using(var session = sessionFactory.OpenSession())

         using(var tx = session.BeginTransaction()) { 
            var query = from customer in session.Query<Customer>() where
               customer.Id == id select customer; 
					
            var reloaded = query.Fetch(x => x.Orders).ToList().First();
            Console.WriteLine("Reloaded:"); Console.WriteLine(reloaded); 

            tx.Commit(); 
         }
			
         Console.WriteLine("Press <ENTER> to exit..."); 
         Console.ReadLine(); 
      }
      
      private static Customer CreateCustomer() { 
         var customer = new Customer { 
            FirstName = "John", 
            LastName = "Doe", 
            Points = 100, 
            HasGoldStatus = true, 
            MemberSince = new DateTime(2012, 1, 1), 
            CreditRating = CustomerCreditRating.Good, 
            AverageRating = 42.42424242, 
            Address = CreateLocation() 
         }; 
			
         var order1 = new Order { Ordered = DateTime.Now }; 
         
         customer.AddOrder(order1); var order2 = new Order {
            Ordered = DateTime.Now.AddDays(-1), 
            Shipped = DateTime.Now, 
            ShipTo = CreateLocation()
         }; 
			
         customer.AddOrder(order2); 
         return customer; 
      }
      
      private static Location CreateLocation() { 
         return new Location { 
            Street = "123 Somewhere Avenue", 
            City = "Nowhere", 
            Province = "Alberta", 
            Country = "Canada" 
         }; 
      }
      
      private static Configuration ConfigureNHibernate() { 
         NHibernateProfiler.Initialize(); 
         var cfg = new Configuration(); 
         
         cfg.DataBaseIntegration(x => { 
            x.ConnectionStringName = "default"; 
            x.Driver<SqlClientDriver>(); 
            x.Dialect<MsSql2008Dialect>(); 
            x.IsolationLevel = IsolationLevel.RepeatableRead; 
            x.Timeout = 10; 
            x.BatchSize = 10; 
         }); 
         
         cfg.SessionFactory().GenerateStatistics();
         cfg.AddAssembly(Assembly.GetExecutingAssembly()); 
         return cfg; 
      } 
   } 
}

Он сохранит это в базе данных, а затем перезагрузит. Теперь давайте запустим ваше приложение, откроем NHibernate Profiler и посмотрим, как оно на самом деле его сохранило.

Вы заметите, что у нас есть 3 группы утверждений. Первый будет вставлять клиента, и идентификатор этого клиента - Guid, который выделен. Вторая инструкция вставляется в таблицу заказов.

Вы заметите, что там установлен тот же идентификатор клиента Guid, поэтому установите этот внешний ключ. Последний оператор - это обновление, которое еще раз обновит внешний ключ до того же идентификатора клиента.

Теперь проблема в том, что у клиента есть заказы, а у заказов есть заказчик, мы никак не могли не сказать NHibernate, что на самом деле это те же отношения. Мы делаем это с обратным равенством true.

Итак, перейдем к нашему customer.hbm.xml сопоставления и установите для обратного значения true, как показано в следующем коде.

<?xml version = "1.0" encoding = "utf-8" ?> 
<hibernate-mapping xmlns = "urn:nhibernate-mapping-2.2" assembly = "NHibernateDemo"
   namespace = "NHibernateDemo"> 
   
   <class name = "Customer">
	
      <id name = "Id"> 
         <generator class = "guid.comb"/> 
      </id> 
      
      <property name = "FirstName"/> 
      <property name = "LastName"/> 
      <property name = "AverageRating"/> 
      <property name = "Points"/> 
      <property name = "HasGoldStatus"/> 
      <property name = "MemberSince" type = "UtcDateTime"/>
      <property name = "CreditRating" type = "CustomerCreditRatingType"/>
      
      <component name = "Address"> 
         <property name = "Street"/> 
         <property name = "City"/> 
         <property name = "Province"/> 
         <property name = "Country"/> 
      </component>
      
      <set name = "Orders" table = "`Order`" cascade = "all-delete-orphan" 
         inverse = "true"> 
         <key column = "CustomerId"/> 
         <one-to-many class = "Order"/> 
      </set> 
   
   </class> 
</hibernate-mapping>

При сохранении заказов он устанавливает этот внешний ключ со стороны заказа. Теперь давайте снова запустим это приложение и откроем профилировщик NHibernate.

Если мы посмотрим, как они вставляются, мы получим вставку в клиенте и вставку в заказы, но у нас нет этого дублирующего обновления внешнего ключа, потому что он обновляется при сохранении заказов.

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

  • Если вы посмотрите на отношения «многие к одному» в Order.hbm.xml файл, и вы ищете обратный, на самом деле у него нет обратного атрибута.

  • Он всегда устанавливается из дочернего элемента, но если у вас есть коллекция «многие ко многим», вы можете установить ее с любой стороны.

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

  • Get - он вернет объект или нуль.

  • Load - он вернет объект или бросит ObjectNotFoundException.

Итак, почему у нас есть эти два разных API?

Загрузить

  • Это потому, что Load может намного более эффективно оптимизировать круговые обходы базы данных.

  • Load фактически возвращает прокси-объект, и ему не нужно обращаться к базе данных сразу после вызова этого Load.

  • Когда вы обращаетесь к этому прокси, объект не находится в базе данных, в этот момент он может вызвать исключение ObjectNotFoundException.

Получить

  • И наоборот, с Get из-за ограничений CLR или Common Language Runtime и NHibernate должен немедленно перейти в базу данных, проверить, есть ли там объекты, и вернуть null, если его нет.

  • У него нет возможности объекта отложить эту выборку, этот обратный путь к базе данных на более позднее время, потому что он не может вернуть прокси-объект и заменил этот прокси-объект на null, когда пользователь фактически обращается к нему.

Давайте рассмотрим простой пример, в котором вы увидите, как они на самом деле используются, и разницу между Get и Load. Мы продолжим с теми же предметными классамиCustomers и Orders и аналогичные файлы сопоставления из предыдущей главы.

В этом примере мы сначала будем использовать Get, как показано в следующей программе.

using System; 
using System.Data; 
using System.Linq; 
using System.Reflection; 

using HibernatingRhinos.Profiler.Appender.NHibernate; 
using NHibernate.Cfg; 
using NHibernate.Criterion; 
using NHibernate.Dialect; 
using NHibernate.Driver;
using NHibernate.Linq; 

namespace NHibernateDemo { 

   internal class Program { 
	
      private static void Main() { 
		
         var cfg = ConfigureNHibernate(); 
         var sessionFactory = cfg.BuildSessionFactory();
         using(var session = sessionFactory.OpenSession()) 
         
         using(var tx = session.BeginTransaction()) { 
            var id1 = Guid.Parse("4e97c816-6bce-11e1-b095-6cf049ee52be"); 
            var id2 = Guid.Parse("AAAAAAAA-BBBB-CCCC-DDDD-EEEEEEEEEEEE");
				
            var customer1 = session.Get<Customer>(id1); 
            Console.WriteLine("Customer1 data"); 
            Console.WriteLine(customer1);
				
            var customer2 = session.Get<Customer>(id2); 
            Console.WriteLine("Customer2 data"); 
            Console.WriteLine(customer2); 
				
            tx.Commit(); 
         }
			
         Console.WriteLine("Press <ENTER> to exit..."); 
         Console.ReadLine(); 
      }
		
      private static Configuration ConfigureNHibernate() {
		
         NHibernateProfiler.Initialize(); 
         var cfg = new Configuration(); 
         
         cfg.DataBaseIntegration(x => { 
            x.ConnectionStringName = "default"; 
            x.Driver<SqlClientDriver>(); 
            x.Dialect<MsSql2008Dialect>(); 
            x.IsolationLevel = IsolationLevel.RepeatableRead;
            x.Timeout = 10; 
            x.BatchSize = 10; 
         }); 
         
         cfg.SessionFactory().GenerateStatistics();
         cfg.AddAssembly(Assembly.GetExecutingAssembly()); 
         return cfg; 
      } 
   } 
}

Как видите, у нас есть два GuidИдентификаторы, первый - хороший идентификатор, это идентификатор клиента, который, как мы знаем, есть в базе данных. Пока второго ID нет в базе. Оба этих идентификатора передаются как параметр вGet() метод, а затем результат выводится на консоль.

Когда приведенный выше код скомпилирован и выполнен, вы увидите следующий результат.

Customer1 data
Laverne Hegmann (4e97c816-6bce-11e1-b095-6cf049ee52be)
   Points: 74
   HasGoldStatus: True
   MemberSince: 4/4/2009 12:00:00 AM (Utc)
   CreditRating: Neutral
   AverageRating: 0

Orders:
   Order Id: 4ea14d96-6bce-11e1-b095-6cf049ee52be
   Order Id: 4ea14d96-6bce-11e1-b096-6cf049ee52be
   Order Id: 4ea14d96-6bce-11e1-b097-6cf049ee52be
   Order Id: 4ea14d96-6bce-11e1-b098-6cf049ee52be
	
Customer2 data
Press <ENTER> to exit...

Как вы можете видеть, данные Customer1 печатаются, но данные Customer2 пусты, это потому, что запись Customer2 недоступна в базе данных.

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

Как видите, данные Customer1 доступны, в то время как Customer2 имеет значение null, а тип - NHibernateDemo.Customer для обоих.

Теперь давайте воспользуемся методом Load вместо Get в том же примере, который показан в следующем коде.

using System; 
using System.Data; 
using System.Linq; 
using System.Reflection; 

using HibernatingRhinos.Profiler.Appender.NHibernate; 
using NHibernate.Cfg; 
using NHibernate.Criterion; 
using NHibernate.Dialect; 
using NHibernate.Driver;
using NHibernate.Linq; 

namespace NHibernateDemo { 

   internal class Program { 
	
      private static void Main() { 
		
         var cfg = ConfigureNHibernate(); 
         var sessionFactory = cfg.BuildSessionFactory();
         using(var session = sessionFactory.OpenSession()) 
         
         using(var tx = session.BeginTransaction()) { 
            var id1 = Guid.Parse("4e97c816-6bce-11e1-b095-6cf049ee52be"); 
            var id2 = Guid.Parse("AAAAAAAA-BBBB-CCCC-DDDD-EEEEEEEEEEEE");
				
            var customer1 = session.Load<Customer>(id1); 
            Console.WriteLine("Customer1 data"); 
            Console.WriteLine(customer1);
				
            var customer2 = session.Load<Customer>(id2); 
            Console.WriteLine("Customer2 data"); 
            Console.WriteLine(customer2); 
				
            tx.Commit(); 
         }
			
         Console.WriteLine("Press <ENTER> to exit..."); 
         Console.ReadLine(); 
      }
		
      private static Configuration ConfigureNHibernate() { 
		
         NHibernateProfiler.Initialize(); 
         var cfg = new Configuration(); 
         
         cfg.DataBaseIntegration(x => { 
            x.ConnectionStringName = "default"; 
            x.Driver<SqlClientDriver>(); 
            x.Dialect<MsSql2008Dialect>(); 
            x.IsolationLevel = IsolationLevel.RepeatableRead; 
            x.Timeout = 10;
            x.BatchSize = 10; 
         }); 
			
         cfg.SessionFactory().GenerateStatistics();
         cfg.AddAssembly(Assembly.GetExecutingAssembly()); 
         return cfg; 
      } 
   } 
}

Теперь давайте запустим этот пример, и вы увидите, что возникает следующее исключение, как показано на снимке экрана.

Теперь, если вы посмотрите в окно Watch, вы увидите, что типом является прокси клиента для обоих объектов. И вы также видите те же данные для Customer1 в окне консоли.

Customer1 data
Laverne Hegmann (4e97c816-6bce-11e1-b095-6cf049ee52be)
   Points: 74
   HasGoldStatus: True
   MemberSince: 4/4/2009 12:00:00 AM (Utc)
   CreditRating: Neutral
   AverageRating: 0

   Orders:
      Order Id: 4ea14d96-6bce-11e1-b095-6cf049ee52be
      Order Id: 4ea14d96-6bce-11e1-b096-6cf049ee52be
      Order Id: 4ea14d96-6bce-11e1-b097-6cf049ee52be
      Order Id: 4ea14d96-6bce-11e1-b098-6cf049ee52be 
		
Customer2 data

В этой главе мы рассмотрим еще один распространенный API, который будут использовать люди, - это провайдер NHibernate LINQ. Доступ к нему через метод расширения в ISession, а подпись являетсяQuery <T>. При использовании LINQ существует два типа синтаксиса:

  • Синтаксис цепочки запросов
  • Синтаксис понимания запросов

Синтаксис цепочки запросов

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

var customer = session.Query<Customer>() .Where(c => c.FirstName == "Laverne")
  • Вы можете видеть, что у нас есть запрос, а также предложение WHERE, у вас могут быть дополнительные предложения WHERE и аналогичное предложение select.

  • Это стандартный синтаксис цепочки методов, который можно использовать в обычном LINQ.

  • LINQ to Objects или LINQ to SQL, любой из других поставщиков LINQ, с которыми вы, возможно, знакомы.

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

using System; 
using System.Data; 
using System.Linq; 
using System.Reflection; 

using HibernatingRhinos.Profiler.Appender.NHibernate; 
using NHibernate.Cfg; 
using NHibernate.Criterion; 
using NHibernate.Dialect; 
using NHibernate.Driver; 
using NHibernate.Linq;

namespace NHibernateDemo { 

   internal class Program {
	
      private static void Main() { 
		
         var cfg = ConfigureNHibernate(); 
         var sessionFactory = cfg.BuildSessionFactory();
         using(var session = sessionFactory.OpenSession()) 
         
         using(var tx = session.BeginTransaction()) {
            var customer = session.Query<Customer>() 
               .Where(c => c.FirstName == "Laverne").First(); 
            Console.WriteLine(customer); 
            tx.Commit(); 
         }
			
         Console.WriteLine("Press <ENTER> to exit..."); 
         Console.ReadLine(); 
      }
		
      private static Configuration ConfigureNHibernate() { 
		
         NHibernateProfiler.Initialize(); 
         var cfg = new Configuration(); 
         
         cfg.DataBaseIntegration(x => { 
            x.ConnectionStringName = "default"; 
            x.Driver<SqlClientDriver>(); 
            x.Dialect<MsSql2008Dialect>(); 
            x.IsolationLevel = IsolationLevel.RepeatableRead; 
            x.Timeout = 10; 
            x.BatchSize = 10; 
         }); 
         
         cfg.SessionFactory().GenerateStatistics();
         cfg.AddAssembly(Assembly.GetExecutingAssembly()); 
         return cfg; 
      } 
   } 
}

Теперь, когда приведенный выше код скомпилирован и выполнен, вы увидите следующий результат.

Laverne Hegmann (4e97c816-6bce-11e1-b095-6cf049ee52be)
   Points: 74
   HasGoldStatus: True
   MemberSince: 4/4/2009 12:00:00 AM (Utc)
   CreditRating: Neutral
   AverageRating: 0

   Orders:
      Order Id: 4ea14d96-6bce-11e1-b095-6cf049ee52be
      Order Id: 4ea14d96-6bce-11e1-b096-6cf049ee52be
      Order Id: 4ea14d96-6bce-11e1-b097-6cf049ee52be
      Order Id: 4ea14d96-6bce-11e1-b098-6cf049ee52be
		
Press <ENTER> to exit...

Синтаксис понимания запросов

Также существует синтаксис понимания запросов, который больше похож на SQL с использованием ключевых слов from, where и select.

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

using System; 
using System.Data; 
using System.Linq; 
using System.Reflection; 

using HibernatingRhinos.Profiler.Appender.NHibernate; 
using NHibernate.Cfg; 
using NHibernate.Criterion; 
using NHibernate.Dialect; 
using NHibernate.Driver; 
using NHibernate.Linq;

namespace NHibernateDemo { 

   internal class Program { 
	
      private static void Main() { 
		
         var cfg = ConfigureNHibernate(); 
         var sessionFactory = cfg.BuildSessionFactory();
         using(var session = sessionFactory.OpenSession()) 
         
         using(var tx = session.BeginTransaction()) {
            var customer = (from c in session.Query<Customer>() 
               where c.FirstName == "Laverne" select c).First();
            Console.WriteLine(customer); 
            tx.Commit();
         }
         
         Console.WriteLine("Press <ENTER> to exit..."); 
         Console.ReadLine(); 
      }
      
      private static Configuration ConfigureNHibernate() {
		
         NHibernateProfiler.Initialize(); 
         var cfg = new Configuration(); 
         
         cfg.DataBaseIntegration(x => { 
            x.ConnectionStringName = "default"; 
            x.Driver<SqlClientDriver>(); 
            x.Dialect<MsSql2008Dialect>(); 
            x.IsolationLevel = IsolationLevel.RepeatableRead; 
            x.Timeout = 10; 
            x.BatchSize = 10; 
         }); 
         
         cfg.SessionFactory().GenerateStatistics();
         cfg.AddAssembly(Assembly.GetExecutingAssembly()); 
         return cfg; 
      } 
   } 
}

Теперь давайте снова запустим это приложение, и вы увидите следующий результат.

Laverne Hegmann (4e97c816-6bce-11e1-b095-6cf049ee52be)
   Points: 74
   HasGoldStatus: True
   MemberSince: 4/4/2009 12:00:00 AM (Utc)
   CreditRating: Neutral
   AverageRating: 0

   Orders:
      Order Id: 4ea14d96-6bce-11e1-b095-6cf049ee52be
      Order Id: 4ea14d96-6bce-11e1-b096-6cf049ee52be
      Order Id: 4ea14d96-6bce-11e1-b097-6cf049ee52be
      Order Id: 4ea14d96-6bce-11e1-b098-6cf049ee52be
		
Press <ENTER> to exit...

Давайте посмотрим на другой пример, в котором мы получим всех тех клиентов, чье имя начинается с буквы H.

using System; 
using System.Data; 
using System.Linq; 
using System.Reflection; 

using HibernatingRhinos.Profiler.Appender.NHibernate; 
using NHibernate.Cfg; 
using NHibernate.Criterion; 
using NHibernate.Dialect; 
using NHibernate.Driver; 
using NHibernate.Linq;

namespace NHibernateDemo { 

   internal class Program { 
	
      private static void Main() { 
		
         var cfg = ConfigureNHibernate(); 
         var sessionFactory = cfg.BuildSessionFactory();
         using(var session = sessionFactory.OpenSession()) 
         
         using(var tx = session.BeginTransaction()) {
            var customers = session.Query<Customer>() .Where(c =<
               c.FirstName.StartsWith("H"));
            
            foreach (var customer in customers.ToList()) { 
               Console.WriteLine(customer); 
            }
				
            tx.Commit(); 
         }
			
         Console.WriteLine("Press <ENTER> to exit..."); 
         Console.ReadLine(); 
      }
		
      private static Configuration ConfigureNHibernate() { 
		
         NHibernateProfiler.Initialize(); 
         var cfg = new Configuration(); 
         
         cfg.DataBaseIntegration(x => { 
            x.ConnectionStringName = "default"; 
            x.Driver<SqlClientDriver>(); 
            x.Dialect<MsSql2008Dialect>(); 
            x.IsolationLevel = IsolationLevel.RepeatableRead; 
            x.Timeout = 10; 
            x.BatchSize = 10; 
         }); 
			
         cfg.SessionFactory().GenerateStatistics();
         cfg.AddAssembly(Assembly.GetExecutingAssembly()); 
         return cfg; 
      } 
   } 
}

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

using System; 
using System.Data; 
using System.Linq; 
using System.Reflection; 

using HibernatingRhinos.Profiler.Appender.NHibernate; 
using NHibernate.Cfg; 
using NHibernate.Criterion; 
using NHibernate.Dialect; 
using NHibernate.Driver; 
using NHibernate.Linq;

namespace NHibernateDemo { 

   internal class Program { 
	
      private static void Main() { 
		
         var cfg = ConfigureNHibernate(); 
         var sessionFactory = cfg.BuildSessionFactory();
         using(var session = sessionFactory.OpenSession()) 
         
         using(var tx = session.BeginTransaction()) {
            var customers = from c in session.Query<Customer>() 
               where c.FirstName.StartsWith("H") select c;
            
            foreach (var customer in customers.ToList()) { 
               Console.WriteLine(customer); 
            }
				
            tx.Commit(); 
         }
			
         Console.WriteLine("Press <ENTER> to exit..."); 
         Console.ReadLine(); 
      }
		
      private static Configuration ConfigureNHibernate() { 
		
         NHibernateProfiler.Initialize(); 
         var cfg = new Configuration(); 
         
         cfg.DataBaseIntegration(x => { 
            x.ConnectionStringName = "default"; 
            x.Driver<SqlClientDriver>(); 
            x.Dialect<MsSql2008Dialect>(); 
            x.IsolationLevel = IsolationLevel.RepeatableRead; 
            x.Timeout = 10; 
            x.BatchSize = 10; 
         }); 
			
         cfg.SessionFactory().GenerateStatistics();
         cfg.AddAssembly(Assembly.GetExecutingAssembly()); 
         return cfg; 
      } 
   }  
}

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

Herman Crooks (4ead3480-6bce-11e1-b15c-6cf049ee52be)
   Points: 74
   HasGoldStatus: True
   MemberSince: 12/3/2010 12:00:00 AM (Utc)
   CreditRating: Neutral
   AverageRating: 0

   Orders:
      Order Id: 4ead3480-6bce-11e1-b15d-6cf049ee52be
      Order Id: 4ead3480-6bce-11e1-b15e-6cf049ee52be
      Order Id: 4ead3480-6bce-11e1-b15f-6cf049ee52be
      Order Id: 4ead3480-6bce-11e1-b160-6cf049ee52be
      Order Id: 4ead3480-6bce-11e1-b161-6cf049ee52be
      Order Id: 4ead3480-6bce-11e1-b162-6cf049ee52be
      Order Id: 4ead3480-6bce-11e1-b163-6cf049ee52be

Hudson Bins (4ec03f80-6bce-11e1-b2b7-6cf049ee52be)
   Points: 56
   HasGoldStatus: False
   MemberSince: 10/20/2008 12:00:00 AM (Utc)
   CreditRating: Terrible
   AverageRating: 0

   Orders:
      Order Id: 4ec03f80-6bce-11e1-b2b8-6cf049ee52be
      Order Id: 4ec03f80-6bce-11e1-b2b9-6cf049ee52be
      Order Id: 4ec03f80-6bce-11e1-b2ba-6cf049ee52be
      Order Id: 4ec03f80-6bce-11e1-b2bb-6cf049ee52be
      Order Id: 4ec03f80-6bce-11e1-b2bc-6cf049ee52be
      Order Id: 4ec03f80-6bce-11e1-b2bd-6cf049ee52be
      Order Id: 4ec03f80-6bce-11e1-b2be-6cf049ee52be
      Order Id: 4ec03f80-6bce-11e1-b2bf-6cf049ee52be

Hettie Feest (4ec50240-6bce-11e1-b300-6cf049ee52be)
   Points: 82
   HasGoldStatus: False
   MemberSince: 4/10/2009 12:00:00 AM (Utc)
   CreditRating: Neutral
   AverageRating: 0

   Orders:
      Order Id: 4ec50240-6bce-11e1-b301-6cf049ee52be
      Order Id: 4ec50240-6bce-11e1-b302-6cf049ee52be
      Order Id: 4ec50240-6bce-11e1-b303-6cf049ee52be
		
Press <ENTER> to exit...

В этой главе мы рассмотрим язык запросов Hibernate. HQL используется как в Java Hibernate, так и в NHibernate.

  • Это самый старый механизм запросов наряду с Criteria.

  • Он был реализован очень рано и представляет собой строковый запрос. API.

  • Вы получаете доступ к нему через ISession CreateQuery, и он почти похож на SQL.

  • Он использует многие из тех же ключевых слов, но имеет упрощенный синтаксис.

  • Это один из наиболее распространенных примеров. Если вы ищете, как выполнить запрос, вы часто найдете примеры HQL.

Ниже приведен простой пример HQL -

var customers = session.CreateQuery("select c from Customer c where c.FirstName = 'Laverne'");
  • Здесь вы можете видеть, что они выбирают C у клиента, это очень похоже на SQL. Что касается NHibernate, это непрозрачная строка, поэтому вы не знаете, является ли это допустимым HQL до времени выполнения, что является одним из недостатков.

  • Одна из сильных сторон поставщика LINQ - это возможность получить поддержку во время компиляции.

  • Но HQL - один из наиболее часто используемых гибких механизмов запросов. Говорят, что если нет другого способа сделать это, то есть способ сделать это в HQL.

Давайте посмотрим на простой пример, в котором мы воссоздадим наши запросы LINQ, используя вместо этого HQL. Вы можете получить доступ к HQL, позвонив вsession.CreateQuery и передать как параметр, используя строку HQL.

using System; 
using System.Data; 
using System.Linq; 
using System.Reflection; 

using HibernatingRhinos.Profiler.Appender.NHibernate; 
using NHibernate.Cfg; 
using NHibernate.Criterion; 
using NHibernate.Dialect; 
using NHibernate.Driver; 
using NHibernate.Linq;

namespace NHibernateDemo {

   internal class Program { 
      
      private static void Main() { 
		
         var cfg = ConfigureNHibernate(); 
         var sessionFactory = cfg.BuildSessionFactory();
         using(var session = sessionFactory.OpenSession()) 
         
         using(var tx = session.BeginTransaction()) { 
            var customers = session.CreateQuery("select c from Customer c 
               where c.FirstName = 'Laverne'"); 
            
            foreach (var customer in customers.List<Customer>()) { 
               Console.WriteLine(customer); 
            }
				
            tx.Commit(); 
         }
			
         Console.WriteLine("Press <ENTER> to exit..."); 
         Console.ReadLine(); 
      }
		
      private static Configuration ConfigureNHibernate() { 
		
         NHibernateProfiler.Initialize(); 
         var cfg = new Configuration(); 
         
         cfg.DataBaseIntegration(x => { 
            x.ConnectionStringName = "default"; 
            x.Driver<SqlClientDriver>(); 
            x.Dialect<MsSql2008Dialect>(); 
            x.IsolationLevel = IsolationLevel.RepeatableRead; 
            x.Timeout = 10; 
            x.BatchSize = 10; 
         }); 
         
         cfg.SessionFactory().GenerateStatistics();
         cfg.AddAssembly(Assembly.GetExecutingAssembly()); 
         return cfg; 
      } 
   } 
}
  • Эта строка HQL очень похожа на SQL, основное отличие состоит в том, что FirstName - это имя свойства, а не имя столбца.

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

  • Если бы внутренняя таблица была названа как Customers, мы все равно использовали бы Customer в нашем HQL-запросе.

Давайте запустим это приложение, и вы увидите следующий результат.

Laverne Hegmann (4e97c816-6bce-11e1-b095-6cf049ee52be)
   Points: 74
   HasGoldStatus: True
   MemberSince: 4/4/2009 12:00:00 AM (Utc)
   CreditRating: Neutral
   AverageRating: 0

   Orders:
      Order Id: 4ea14d96-6bce-11e1-b095-6cf049ee52be
      Order Id: 4ea14d96-6bce-11e1-b096-6cf049ee52be
      Order Id: 4ea14d96-6bce-11e1-b097-6cf049ee52be
      Order Id: 4ea14d96-6bce-11e1-b098-6cf049ee52be
		
Press <ENTER> to exit...

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

using System; 
using System.Data; 
using System.Linq; 
using System.Reflection; 

using HibernatingRhinos.Profiler.Appender.NHibernate; 
using NHibernate.Cfg; 
using NHibernate.Criterion; 
using NHibernate.Dialect; 
using NHibernate.Driver;
using NHibernate.Linq; 

namespace NHibernateDemo { 

   internal class Program {
	
      private static void Main() { 
		
         var cfg = ConfigureNHibernate(); 
         var sessionFactory = cfg.BuildSessionFactory();
         using(var session = sessionFactory.OpenSession()) 
         
         using(var tx = session.BeginTransaction()) { 
            var customers = session.CreateQuery("select c from Customer c 
               where c.FirstName like 'H%'"); 
            
            foreach (var customer in customers.List<Customer>()) { 
               Console.WriteLine(customer); 
            }
				
            tx.Commit(); 
         } 
			
         Console.WriteLine("Press <ENTER> to exit..."); 
         Console.ReadLine(); 
      } 
		
      private static Configuration ConfigureNHibernate() {
		
         NHibernateProfiler.Initialize(); 
         var cfg = new Configuration(); 
         
         cfg.DataBaseIntegration(x => { 
            x.ConnectionStringName = "default"; 
            x.Driver<SqlClientDriver>(); 
            x.Dialect<MsSql2008Dialect>(); 
            x.IsolationLevel = IsolationLevel.RepeatableRead; 
            x.Timeout = 10; 
            x.BatchSize = 10; 
         }); 
			
         cfg.SessionFactory().GenerateStatistics();
         cfg.AddAssembly(Assembly.GetExecutingAssembly()); 
         return cfg; 
      } 
   } 
}

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

Herman Crooks (4ead3480-6bce-11e1-b15c-6cf049ee52be)
   Points: 74
   HasGoldStatus: True
   MemberSince: 12/3/2010 12:00:00 AM (Utc)
   CreditRating: Neutral
   AverageRating: 0

   Orders:
      Order Id: 4ead3480-6bce-11e1-b15d-6cf049ee52be
      Order Id: 4ead3480-6bce-11e1-b15e-6cf049ee52be
      Order Id: 4ead3480-6bce-11e1-b15f-6cf049ee52be
      Order Id: 4ead3480-6bce-11e1-b160-6cf049ee52be
      Order Id: 4ead3480-6bce-11e1-b161-6cf049ee52be
      Order Id: 4ead3480-6bce-11e1-b162-6cf049ee52be
      Order Id: 4ead3480-6bce-11e1-b163-6cf049ee52be

Hudson Bins (4ec03f80-6bce-11e1-b2b7-6cf049ee52be)
   Points: 56
   HasGoldStatus: False
   MemberSince: 10/20/2008 12:00:00 AM (Utc)
   CreditRating: Terrible
   AverageRating: 0

   Orders:
      Order Id: 4ec03f80-6bce-11e1-b2b8-6cf049ee52be
      Order Id: 4ec03f80-6bce-11e1-b2b9-6cf049ee52be
      Order Id: 4ec03f80-6bce-11e1-b2ba-6cf049ee52be
      Order Id: 4ec03f80-6bce-11e1-b2bb-6cf049ee52be
      Order Id: 4ec03f80-6bce-11e1-b2bc-6cf049ee52be
      Order Id: 4ec03f80-6bce-11e1-b2bd-6cf049ee52be
      Order Id: 4ec03f80-6bce-11e1-b2be-6cf049ee52be
      Order Id: 4ec03f80-6bce-11e1-b2bf-6cf049ee52be

Hettie Feest (4ec50240-6bce-11e1-b300-6cf049ee52be)
   Points: 82
   HasGoldStatus: False
   MemberSince: 4/10/2009 12:00:00 AM (Utc)
   CreditRating: Neutral
   AverageRating: 0

   Orders:
      Order Id: 4ec50240-6bce-11e1-b301-6cf049ee52be
      Order Id: 4ec50240-6bce-11e1-b302-6cf049ee52be
      Order Id: 4ec50240-6bce-11e1-b303-6cf049ee52be
		
Press <ENTER> to exit...

Мы можем делать более сложные вещи, например запрашивать все заказы, в которых количество клиентов превышает 9. Ниже приводится запрос HQL для тех же.

var customers = session.CreateQuery("select c from Customer c 
   where size(c.Orders) > 9"); 
	
foreach (var customer in customers.List<Customer>()) { 
   Console.WriteLine(customer); 
}

Нам также нужно указать, что здесь нам нужен размер или количество или длина. В HQL у нас есть возможность использовать метод специального размера, как показано выше.

Другой способ написать это, если хотите, - c.Orders.size, и это имеет точный эффект.

var customers = session.CreateQuery("select c from Customer c 
   where c.Orders.size > 9"); 
	
foreach (var customer in customers.List<Customer>()) { 
   Console.WriteLine(customer); 
}

Запустим это приложение.

Lindsay Towne (4ea3aef6-6bce-11e1-b0cb-6cf049ee52be)
   Points: 50
   HasGoldStatus: False
   MemberSince: 4/13/2007 12:00:00 AM (Utc)
   CreditRating: VeryGood
   AverageRating: 0

   Orders:
      Order Id: 4ea3aef6-6bce-11e1-b0cc-6cf049ee52be
      Order Id: 4ea3aef6-6bce-11e1-b0cd-6cf049ee52be
      Order Id: 4ea3aef6-6bce-11e1-b0ce-6cf049ee52be
      Order Id: 4ea3aef6-6bce-11e1-b0cf-6cf049ee52be
      Order Id: 4ea3aef6-6bce-11e1-b0d0-6cf049ee52be
      Order Id: 4ea3aef6-6bce-11e1-b0d1-6cf049ee52be
      Order Id: 4ea3aef6-6bce-11e1-b0d2-6cf049ee52be
      Order Id: 4ea3aef6-6bce-11e1-b0d3-6cf049ee52be
      Order Id: 4ea3aef6-6bce-11e1-b0d4-6cf049ee52be
      Order Id: 4ea3aef6-6bce-11e1-b0d5-6cf049ee52be

Wyman Hammes (4ea61056-6bce-11e1-b0e2-6cf049ee52be)
   Points: 32
   HasGoldStatus: False
   MemberSince: 2/5/2011 12:00:00 AM (Utc)
   CreditRating: Good
   AverageRating: 0

   Orders:
      Order Id: 4ea61056-6bce-11e1-b0e3-6cf049ee52be
      Order Id: 4ea61056-6bce-11e1-b0e4-6cf049ee52be
      Order Id: 4ea61056-6bce-11e1-b0e5-6cf049ee52be
      Order Id: 4ea61056-6bce-11e1-b0e6-6cf049ee52be
      Order Id: 4ea61056-6bce-11e1-b0e7-6cf049ee52be
      Order Id: 4ea61056-6bce-11e1-b0e8-6cf049ee52be
      Order Id: 4ea61056-6bce-11e1-b0e9-6cf049ee52be
      Order Id: 4ea61056-6bce-11e1-b0ea-6cf049ee52be
      Order Id: 4ea61056-6bce-11e1-b0eb-6cf049ee52be
      Order Id: 4ea61056-6bce-11e1-b0ec-6cf049ee52be
		
Press <ENTER> to exit...

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

В этой главе мы рассмотрим механизм критериальных запросов. ВNHibernate Query by Criteria API позволяет создавать запросы, манипулируя объектами критериев во время выполнения.

  • Этот подход позволяет вам указывать ограничения динамически без прямых манипуляций со строками, но при этом не теряется большая часть гибкости или мощности HQL.

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

  • Классический синтаксис критериев - это объектно-ориентированный API запросов, как показано в следующей программе.

var customers = session.CreateCriteria<Customer>().Add(Restrictions.Like("FirstName", "H%"));
  • Как видите, мы выполняем критерии создания сеанса для клиента, и теперь мы добавляем объект ограничения к этому запросу.

  • Это полезно для страниц запросов, где пользователи могут выбирать одни параметры, но не другие.

  • Проще создать запрос в виде древовидной структуры запроса, чем в HQL или LINQ, где вы можете использовать AND или OR в предложении WHERE.

  • С помощью этих объектов критериев проще просто добавить дополнительные ограничения.

Давайте рассмотрим простой пример, в котором мы создадим запрос и получим доступ к API критериев через createCriteria а затем добавьте ограничение, что имя должно начинаться с H.

using System; 
using System.Data; 
using System.Linq; 
using System.Reflection; 

using HibernatingRhinos.Profiler.Appender.NHibernate; 
using NHibernate.Cfg; 
using NHibernate.Criterion; 
using NHibernate.Dialect; 
using NHibernate.Driver; 
using NHibernate.Linq; 

namespace NHibernateDemo {

   internal class Program { 
      
      private static void Main() { 
		
         var cfg = ConfigureNHibernate(); 
         var sessionFactory = cfg.BuildSessionFactory();
         using(var session = sessionFactory.OpenSession()) 
         
         using(var tx = session.BeginTransaction()) { 
            var customers = session.CreateCriteria<Customer>() 
               .Add(Restrictions.Like("FirstName", "H%")); 
         
            foreach (var customer in customers.List<Customer>()) { 
               Console.WriteLine(customer); 
            } 
				
            tx.Commit(); 
         }
			
         Console.WriteLine("Press <ENTER> to exit..."); 
         Console.ReadLine(); 
      } 
		
      private static Configuration ConfigureNHibernate() {
		
         NHibernateProfiler.Initialize(); 
         var cfg = new Configuration(); 
         
         cfg.DataBaseIntegration(x => { 
            x.ConnectionStringName = "default"; 
            x.Driver<SqlClientDriver>(); 
            x.Dialect<MsSql2008Dialect>(); 
            x.IsolationLevel = IsolationLevel.RepeatableRead; 
            x.Timeout = 10; 
            x.BatchSize = 10; 
         }); 
			
         cfg.SessionFactory().GenerateStatistics();
         cfg.AddAssembly(Assembly.GetExecutingAssembly()); 
         return cfg; 
      } 
   } 
}

Когда приведенный выше код скомпилирован и выполнен, вы увидите следующий результат.

Herman Crooks (4ead3480-6bce-11e1-b15c-6cf049ee52be)
   Points: 74
   HasGoldStatus: True
   MemberSince: 12/3/2010 12:00:00 AM (Utc)
   CreditRating: Neutral
   AverageRating: 0

   Orders:
      Order Id: 4ead3480-6bce-11e1-b15d-6cf049ee52be
      Order Id: 4ead3480-6bce-11e1-b15e-6cf049ee52be
      Order Id: 4ead3480-6bce-11e1-b15f-6cf049ee52be
      Order Id: 4ead3480-6bce-11e1-b160-6cf049ee52be
      Order Id: 4ead3480-6bce-11e1-b161-6cf049ee52be
      Order Id: 4ead3480-6bce-11e1-b162-6cf049ee52be
      Order Id: 4ead3480-6bce-11e1-b163-6cf049ee52be

Hudson Bins (4ec03f80-6bce-11e1-b2b7-6cf049ee52be)
   Points: 56
   HasGoldStatus: False
   MemberSince: 10/20/2008 12:00:00 AM (Utc)
   CreditRating: Terrible
   AverageRating: 0

   Orders:
      Order Id: 4ec03f80-6bce-11e1-b2b8-6cf049ee52be
      Order Id: 4ec03f80-6bce-11e1-b2b9-6cf049ee52be
      Order Id: 4ec03f80-6bce-11e1-b2ba-6cf049ee52be
      Order Id: 4ec03f80-6bce-11e1-b2bb-6cf049ee52be
      Order Id: 4ec03f80-6bce-11e1-b2bc-6cf049ee52be
      Order Id: 4ec03f80-6bce-11e1-b2bd-6cf049ee52be
      Order Id: 4ec03f80-6bce-11e1-b2be-6cf049ee52be
      Order Id: 4ec03f80-6bce-11e1-b2bf-6cf049ee52be

Hettie Feest (4ec50240-6bce-11e1-b300-6cf049ee52be)
   Points: 82
   HasGoldStatus: False
   MemberSince: 4/10/2009 12:00:00 AM (Utc)
   CreditRating: Neutral
   AverageRating: 0

   Orders:
      Order Id: 4ec50240-6bce-11e1-b301-6cf049ee52be
      Order Id: 4ec50240-6bce-11e1-b302-6cf049ee52be
      Order Id: 4ec50240-6bce-11e1-b303-6cf049ee52be
		
Press <ENTER> to exit…

Давайте рассмотрим еще один простой пример, в котором мы получим клиента, имя которого равно «Laverne».

using System; 
using System.Data; 
using System.Linq; 
using System.Reflection; 

using HibernatingRhinos.Profiler.Appender.NHibernate; 
using NHibernate.Cfg; 
using NHibernate.Criterion; 
using NHibernate.Dialect; 
using NHibernate.Driver; 
using NHibernate.Linq;

namespace NHibernateDemo { 

   internal class Program { 
      
      private static void Main() { 
		
         var cfg = ConfigureNHibernate(); 
         var sessionFactory = cfg.BuildSessionFactory();
         using(var session = sessionFactory.OpenSession()) 
			
         using(var tx = session.BeginTransaction()) { 
            var customers = session.CreateCriteria<Customer>() 
               .Add(Restrictions.Eq("FirstName", "Laverne")) .List<Customer>(); 
            
            foreach (var customer in customers) {
               Console.WriteLine(customer); 
            } 
				
            tx.Commit(); 
         }
			
         Console.WriteLine("Press <ENTER> to exit..."); 
         Console.ReadLine(); 
      }
		
      private static Configuration ConfigureNHibernate() {
		
         NHibernateProfiler.Initialize(); 
         var cfg = new Configuration(); 
         
         cfg.DataBaseIntegration(x => { 
            x.ConnectionStringName = "default"; 
            x.Driver<SqlClientDriver>(); 
            x.Dialect<MsSql2008Dialect>(); 
            x.IsolationLevel = IsolationLevel.RepeatableRead; 
            x.Timeout = 10; 
            x.BatchSize = 10; 
         }); 
         
         cfg.SessionFactory().GenerateStatistics();
         cfg.AddAssembly(Assembly.GetExecutingAssembly()); 
         return cfg; 
      } 
   } 
}

Давайте снова запустим это приложение, и вы увидите следующий результат.

Laverne Hegmann (4e97c816-6bce-11e1-b095-6cf049ee52be)
   Points: 74
   HasGoldStatus: True
   MemberSince: 4/4/2009 12:00:00 AM (Utc)
   CreditRating: Neutral
   AverageRating: 0

   Orders:
      Order Id: 4ea14d96-6bce-11e1-b095-6cf049ee52be
      Order Id: 4ea14d96-6bce-11e1-b096-6cf049ee52be
      Order Id: 4ea14d96-6bce-11e1-b097-6cf049ee52be
      Order Id: 4ea14d96-6bce-11e1-b098-6cf049ee52be
		
Press <ENTER> to exit...

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

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

var customers = session.QueryOver<Customer>() .Where(x => x.FirstName == "Laverne");
  • Это все еще скрытые критерии, но теперь наши запросы строго типизированы.

  • Как мы видели в запросе критериев, первое имя - это просто непрозрачная строка, теперь мы фактически используем x.FirstName, поэтому первое имя подвергается рефакторингу и переименованию, которое изменяется в запросе критериев стиля ссылки с использованием запроса over.

  • Мы все еще можем делать много похожих вещей, но вы не можете использовать синтаксис понимания запроса с query over, вы должны использовать синтаксис цепочки методов, и вы не можете смешивать и сопоставлять ссылку и критерии.

  • Для большого количества запросов запрос через API очень полезен и обеспечивает гораздо более простой для понимания синтаксис объекта, чем использование критериев напрямую.

Давайте рассмотрим простой пример, в котором мы получим клиента по имени Лаверн с помощью запроса.

using System; 
using System.Data; 
using System.Linq; 
using System.Reflection; 

using HibernatingRhinos.Profiler.Appender.NHibernate; 
using NHibernate.Cfg; 
using NHibernate.Criterion; 
using NHibernate.Dialect; 
using NHibernate.Driver; 
using NHibernate.Linq;

namespace NHibernateDemo { 

   internal class Program { 
      
      private static void Main() { 
		
         var cfg = ConfigureNHibernate(); 
         var sessionFactory = cfg.BuildSessionFactory();
         using(var session = sessionFactory.OpenSession()) 
         
         using(var tx = session.BeginTransaction()) { 
            var customers = session.QueryOver<Customer>() 
               .Where(x => x.FirstName == "Laverne"); 
            
            foreach (var customer in customers.List()) { 
               Console.WriteLine(customer); 
            } 
				
            tx.Commit(); 
         }
			
         Console.WriteLine("Press <ENTER> to exit..."); 
         Console.ReadLine(); 
      }
      
      private static Configuration ConfigureNHibernate() { 
		
         NHibernateProfiler.Initialize();
         var cfg = new Configuration(); 
         
         cfg.DataBaseIntegration(x => { 
            x.ConnectionStringName = "default"; 
            x.Driver<SqlClientDriver>(); 
            x.Dialect<MsSql2008Dialect>(); 
            x.IsolationLevel = IsolationLevel.RepeatableRead; 
            x.Timeout = 10; 
            x.BatchSize = 10; 
         });
			
         cfg.SessionFactory().GenerateStatistics();
         cfg.AddAssembly(Assembly.GetExecutingAssembly()); 
         return cfg; 
      } 
   } 
}

Как видите, это все еще скрытые критерии, но это просто более приятный синтаксис.

Когда приведенный выше код скомпилирован и выполнен, вы увидите следующий результат.

Laverne Hegmann (4e97c816-6bce-11e1-b095-6cf049ee52be)
   Points: 74
   HasGoldStatus: True
   MemberSince: 4/4/2009 12:00:00 AM (Utc)
   CreditRating: Neutral
   AverageRating: 0

   Orders:
      Order Id: 4ea14d96-6bce-11e1-b095-6cf049ee52be
      Order Id: 4ea14d96-6bce-11e1-b096-6cf049ee52be
      Order Id: 4ea14d96-6bce-11e1-b097-6cf049ee52be
      Order Id: 4ea14d96-6bce-11e1-b098-6cf049ee52be
		
Press <ENTER> to exit...

Одним из недостатков является то, что, допустим, мы хотим сказать, что FirstName.StartsWith(“A”) как показано в следующей программе.

var customers = session.QueryOver<Customer>() .Where(x => x.FirstName.StartsWith("A"));
 
foreach (var customer in customers.List()) { 
   Console.WriteLine(customer); 
} 

tx.Commit();

Теперь давайте снова запустим приложение, и вы увидите, что это не поставщик LINQ, поскольку он не знает, что это StartsWith метод есть, поэтому вы получите RunTime exception.

Исключение говорит о нераспознанном вызове метода. Здесь мы делаем очевидную вещь, но это не обязательно работает.

Давайте попробуем что-нибудь еще, например, FirstName равно «A%», как показано в следующем коде.

var customers = session.QueryOver<Customer>() .Where(x => x.FirstName == "A%"); 

foreach (var customer in customers.List()) { 
   Console.WriteLine(customer); 
}

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

Press <ENTER> to exit...

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

Как видите, имя равно A%, чего нет. % Используется в SQL с оператором like. Теперь нам нужно создать ограничение для предложения WHERE, как показано в следующей программе.

var customers = session.QueryOver<Customer>() 
   .Where(Restrictions.On<Customer>(c => c.FirstName).IsLike("A%")); 
	
foreach (var customer in customers.List()) { 
   Console.WriteLine(customer); 
}

Давайте снова запустим ваше приложение, и вы увидите, что все клиенты получены с именем, начинающимся с A.

Alejandrin Will (4ea3aef6-6bce-11e1-b0b4-6cf049ee52be)
   Points: 24
   HasGoldStatus: False
   MemberSince: 10/1/2011 12:00:00 AM (Utc)
   CreditRating: VeryVeryGood
   AverageRating: 0

   Orders:
      Order Id: 4ea3aef6-6bce-11e1-b0b5-6cf049ee52be

Austyn Nolan (4ea871b6-6bce-11e1-b110-6cf049ee52be)
   Points: 67
   HasGoldStatus: True
   MemberSince: 12/29/2007 12:00:00 AM (Utc)
   CreditRating: Neutral
   AverageRating: 0

   Orders:
      Order Id: 4ea871b6-6bce-11e1-b111-6cf049ee52be

Antonia Murphy (4ea871b6-6bce-11e1-b121-6cf049ee52be)
   Points: 72
   HasGoldStatus: True
   MemberSince: 6/15/2009 12:00:00 AM (Utc)
   CreditRating: Terrible
   AverageRating: 0

   Orders:
      Order Id: 4ea871b6-6bce-11e1-b122-6cf049ee52be
      Order Id: 4ea871b6-6bce-11e1-b123-6cf049ee52be
      Order Id: 4ea871b6-6bce-11e1-b124-6cf049ee52be
      Order Id: 4ea871b6-6bce-11e1-b125-6cf049ee52be
      Order Id: 4ea871b6-6bce-11e1-b126-6cf049ee52be
      Order Id: 4ea871b6-6bce-11e1-b127-6cf049ee52be
      Order Id: 4ea871b6-6bce-11e1-b128-6cf049ee52be
      Order Id: 4ea871b6-6bce-11e1-b129-6cf049ee52be
      Order Id: 4ea871b6-6bce-11e1-b12a-6cf049ee52be

Он работает так же, как и раньше, за исключением использования этого нового QueryOverсинтаксис. Многие разработчики считают синтаксис LINQ более доступным и часто делает правильные вещи.

Если LINQ не может с этим справиться, вы начнете смотреть на HQL или Criteria, чтобы увидеть, будет ли это более подходящим.

Он просто дает вам другой синтаксис, поэтому Criteria, как критерии создания, так и QueryOver предоставляют вам еще один механизм запросов, который позволяет вам извлекать данные из базы данных с помощью NHibernate.

В этой главе мы расскажем, как использовать собственные SQL-запросы в NHibernate. Если вы уже несколько лет используете рукописный SQL, вы можете быть обеспокоены тем, что ORM лишит вас выразительности и гибкости, к которым вы привыкли.

  • Мощные средства запросов NHibernate позволяют делать почти все, что вы делали бы в SQL, а в некоторых случаях и больше.

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

  • NHibernate позволяет получать объекты, используя собственный диалект SQL вашей базы данных.

Давайте посмотрим на простой пример запросов Native SQL в NHibernate.

using System; 
using System.Data; 
using System.Linq; 
using System.Reflection; 

using HibernatingRhinos.Profiler.Appender.NHibernate; 
using NHibernate.Cfg; 
using NHibernate.Criterion; 
using NHibernate.Dialect; 
using NHibernate.Driver; 
using NHibernate.Linq; 
using NHibernate;

namespace NHibernateDemo {

   internal class Program { 
	
      private static void Main() { 
		
         var cfg = ConfigureNHibernate(); 
         var sessionFactory = cfg.BuildSessionFactory();
         using(var session = sessionFactory.OpenSession()) 
         
         using(var tx = session.BeginTransaction()) {
            IQuery sqlQuery = session.CreateSQLQuery("SELECT * FROM
               CUSTOMER").AddEntity(typeof(Customer));
            var customers = sqlQuery.List<Customer>();
				
            foreach (var customer in customers) { 
               Console.WriteLine(customer); 
            } 
				
            tx.Commit(); 
         }
         
         Console.WriteLine("Press <ENTER> to exit..."); 
         Console.ReadLine(); 
      }
		
      private static Configuration ConfigureNHibernate() { 
		
         NHibernateProfiler.Initialize(); 
         var cfg = new Configuration(); 
         
         cfg.DataBaseIntegration(x => { 
            x.ConnectionStringName = "default"; 
            x.Driver<SqlClientDriver>(); 
            x.Dialect<MsSql2008Dialect>(); 
            x.IsolationLevel = IsolationLevel.RepeatableRead; 
            x.Timeout = 10; 
            x.BatchSize = 10; 
         }); 
         
         cfg.SessionFactory().GenerateStatistics();
         cfg.AddAssembly(Assembly.GetExecutingAssembly()); 
         return cfg; 
      } 
   } 
}

В приведенном выше примере используется CreateSQLQuery() чтобы вернуть список объектов, и вы также заметите, что тип корневой сущности, которую вы хотите вернуть запросом, указан как Customer.

Давайте запустим ваше приложение, и вы увидите, что все клиенты извлечены из базы данных.

Emerson Prosacco (4ec2a0e0-6bce-11e1-b2cf-6cf049ee52be)
   Points: 17
   HasGoldStatus: False
   MemberSince: 6/22/2007 12:00:00 AM (Utc)
   CreditRating: Excellent
   AverageRating: 0

   Orders:
      Order Id: 4ec2a0e0-6bce-11e1-b2d0-6cf049ee52be
      Order Id: 4ec2a0e0-6bce-11e1-b2d1-6cf049ee52be
      Order Id: 4ec2a0e0-6bce-11e1-b2d2-6cf049ee52be
      Order Id: 4ec2a0e0-6bce-11e1-b2d3-6cf049ee52be
      Order Id: 4ec2a0e0-6bce-11e1-b2d4-6cf049ee52be

Kaci Friesen (4ec2a0e0-6bce-11e1-b2d5-6cf049ee52be)
   Points: 30
   HasGoldStatus: True
   MemberSince: 5/25/2007 12:00:00 AM (Utc)
   CreditRating: VeryVeryGood
   AverageRating: 0

   Orders:
      Order Id: 4ec2a0e0-6bce-11e1-b2d6-6cf049ee52be
      Order Id: 4ec2a0e0-6bce-11e1-b2d7-6cf049ee52be
      Order Id: 4ec2a0e0-6bce-11e1-b2d8-6cf049ee52be
      Order Id: 4ec2a0e0-6bce-11e1-b2d9-6cf049ee52be
      Order Id: 4ec2a0e0-6bce-11e1-b2da-6cf049ee52be
      Order Id: 4ec2a0e0-6bce-11e1-b2db-6cf049ee52be

Eveline Waters (4ec2a0e0-6bce-11e1-b2dc-6cf049ee52be)
   Points: 58
   HasGoldStatus: False
   MemberSince: 10/29/2009 12:00:00 AM (Utc)
   CreditRating: Good
   AverageRating: 0

   Orders:
      Order Id: 4ec2a0e0-6bce-11e1-b2dd-6cf049ee52be
      Order Id: 4ec2a0e0-6bce-11e1-b2de-6cf049ee52be
      Order Id: 4ec2a0e0-6bce-11e1-b2df-6cf049ee52be
      Order Id: 4ec2a0e0-6bce-11e1-b2e0-6cf049ee52be
      Order Id: 4ec2a0e0-6bce-11e1-b2e2-6cf049ee52be

Molly Kuhn (4ec2a0e0-6bce-11e1-b2e3-6cf049ee52be)
   Points: 73
   HasGoldStatus: False
   MemberSince: 12/16/2007 12:00:00 AM (Utc)
   CreditRating: VeryGood
   AverageRating: 0

   Orders:
      Order Id: 4ec2a0e0-6bce-11e1-b2e4-6cf049ee52be
      Order Id: 4ec2a0e0-6bce-11e1-b2e5-6cf049ee52be
      Order Id: 4ec2a0e0-6bce-11e1-b2e6-6cf049ee52be
      Order Id: 4ec2a0e0-6bce-11e1-b2e7-6cf049ee52be
      Order Id: 4ec2a0e0-6bce-11e1-b2e8-6cf049ee52be
      Order Id: 4ec2a0e0-6bce-11e1-b2e9-6cf049ee52be
      Order Id: 4ec2a0e0-6bce-11e1-b2ea-6cf049ee52be
      Order Id: 4ec2a0e0-6bce-11e1-b2eb-6cf049ee52be
      Order Id: 4ec2a0e0-6bce-11e1-b2ec-6cf049ee52be

Вот еще один способ написания собственного SQL-запроса, как показано ниже.

IList<Customer> customers = session.CreateSQLQuery("SELECT * FROM CUSTOMER")
   .AddScalar("Id", NHibernateUtil.Guid) 
   .AddScalar("FirstName", NHibernateUtil.String) 
   .AddScalar("LastName", NHibernateUtil.String) .List<Customer>();
  • Как видите, в приведенном выше запросе указаны строка запроса SQL, а также возвращаемые столбцы и типы.

  • Это вернет список массивов объектов со скалярными значениями для каждого столбца в таблице клиентов.

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

Давайте посмотрим на еще один простой пример.

IList<Customer> customers = session.CreateSQLQuery("SELECT * FROM CUSTOMER WHERE  
   FirstName = 'Laverne'") 
   .AddEntity(typeof(Customer)) .List<Customer>(); 
	
foreach (var customer in customers) { 
   Console.WriteLine(customer); 
}

Давайте снова запустим ваше приложение, и вы увидите следующий результат.

Laverne Hegmann (4e97c816-6bce-11e1-b095-6cf049ee52be)
   Points: 74
   HasGoldStatus: True
   MemberSince: 4/4/2009 12:00:00 AM (Utc)
   CreditRating: Neutral
   AverageRating: 0

   Orders:
      Order Id: 4ea14d96-6bce-11e1-b095-6cf049ee52be
      Order Id: 4ea14d96-6bce-11e1-b096-6cf049ee52be
      Order Id: 4ea14d96-6bce-11e1-b097-6cf049ee52be
      Order Id: 4ea14d96-6bce-11e1-b098-6cf049ee52be
		
Press <ENTER> to exit...

Точно так же вы можете указать любой тип SQL-запроса для извлечения данных из базы данных.

В этой главе мы рассмотрим свободный доступ к NHibernate. Fluent NHibernate - это еще один способ сопоставления или, можно сказать, альтернатива стандартным файлам сопоставления XML NHibernate. Вместо написания XML(.hbm.xml files)документы. С помощью Fluent NHibernate вы можете писать сопоставления в строго типизированном коде C #.

  • В Fluent сопоставления NHibernate компилируются вместе с остальной частью вашего приложения.

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

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

  • Вы также можете указать, как объекты должны называться один раз, а все остальное сделает Fluent NHibernate.

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

Установить Fluent NHibernate

Первым шагом для запуска Fluent NHibernate является установка пакета Fluent NHibernate. Так что откройтеNuGet Package Manager Console и введите следующую команду.

PM> install-package FluentNHibernate

После успешной установки вы увидите следующее сообщение.

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

using System; 
using System.Collections.Generic; 
using System.Linq;
using System.Text; 
using System.Threading.Tasks;

namespace FluentNHibernateDemo { 
   class Customer { 
      public virtual int Id { get; set; } 
      public virtual string FirstName { get; set; } 
      public virtual string LastName { get; set; } 
   } 
}

Теперь нам нужно создать сопоставления, используя свободный NHibernate, поэтому добавьте еще один класс CustomerMapв вашем проекте. Вот реализация класса CustomerMap.

using FluentNHibernate.Mapping; 
using System; 
using System.Collections.Generic; 
using System.Linq; using System.Text; 
using System.Threading.Tasks;

namespace FluentNHibernateDemo { 
   class CustomerMap : ClassMap<Customer> { 
      public CustomerMap() { 
         Id(x => x.Id); 
         Map(x => x.FirstName); 
         Map(x => x.LastName); 
         Table("Customer"); 
      } 
   }
}

Добавим еще один класс NHibernateHelper в котором мы будем устанавливать различные параметры конфигурации.

using FluentNHibernate.Cfg; 
using FluentNHibernate.Cfg.Db; 
using NHibernate; 
using NHibernate.Tool.hbm2ddl;

namespace FluentNHibernateDemo { 

   public class NHibernateHelper { 
	
      private static ISessionFactory _sessionFactory;
		
      private static ISessionFactory SessionFactory { 
         get { 
            if (_sessionFactory == null)
            InitializeSessionFactory(); return _sessionFactory; 
         } 
      }
   
      private static void InitializeSessionFactory() { 
         _sessionFactory = Fluently.Configure() 
			
         String Data Source = asia13797\\sqlexpress;
         String Initial Catalog = NHibernateDemoDB;
         String Integrated Security = True;
         String Connect Timeout = 15;
         String Encrypt = False;
         String TrustServerCertificate = False;
         String ApplicationIntent = ReadWrite;
         String MultiSubnetFailover = False;
			
         .Database(MsSqlConfiguration.MsSql2008 .ConnectionString( 
            @"Data Source + Initial Catalog + Integrated Security + Connect Timeout
            + Encrypt + TrustServerCertificate + ApplicationIntent + 
            MultiSubnetFailover") .ShowSql() ) 
				
         .Mappings(m => m.FluentMappings
         .AddFromAssemblyOf<Program>()) 
         .ExposeConfiguration(cfg => new SchemaExport(cfg) 
         .Create(true, true)) 
         .BuildSessionFactory(); 
      }
		
      public static ISession OpenSession() { 
         return SessionFactory.OpenSession(); 
      } 
   }
}

Теперь перейдем к Program.cs файл, в котором мы начнем сеанс, а затем создадим нового клиента и сохраним этого клиента в базе данных, как показано ниже.

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.Threading.Tasks;

namespace FluentNHibernateDemo { 
   class Program { 
	
      static void Main(string[] args) { 
		
         using (var session = NHibernateHelper.OpenSession()) { 
			
            using (var transaction = session.BeginTransaction()) { 
               var customer = new Customer { 
                  FirstName = "Allan", 
                  LastName = "Bomer" 
               }; 
					
               session.Save(customer); 
               transaction.Commit();
               Console.WriteLine("Customer Created: " + customer.FirstName + "\t" +
                  customer.LastName); 
            } 
				
            Console.ReadKey(); 
         } 
      } 
   } 
}

Давайте запустим ваше приложение, и вы увидите следующий результат.

if exists (select * from dbo.sysobjects where id = object_id(N'Customer') and
   OBJECTPROPERTY(id, N'IsUserTable') = 1) drop table Customer

create table Customer (
   Id INT IDENTITY NOT NULL,
   FirstName NVARCHAR(255) null,
   LastName NVARCHAR(255) null,
   primary key (Id)
)

NHibernate: INSERT INTO Customer (FirstName, LastName) VALUES (@p0, @p1); 
   select SCOPE_IDENTITY();@p0 = 'Allan' [Type: String (4000)], 
   @p1 = 'Bomer' [Type: String (4000)]
   Customer Created: Allan Bomer

Как видите, новый клиент создан. Чтобы увидеть запись о клиенте, давайте зайдем в базу данных и посмотрим данные просмотра, и вы увидите, что добавлен 1 клиент.