Nhibernate: Mapping options, query options

    In this article, I decided to put together all the information that I regularly access, rummaging on the Internet or in code. These are mappings and links in NHibernate. A sort of memo article will be. I decided not to overload it too much (for example, I wrote very little about NHibernate Queries), and therefore in each heading there will be a link to the article (in English) that I relied on when creating the memo article. I hope she will be useful to you.


    I will give examples for ASP.NET MVC 4 and SQL Server 2008 (by the way, we will very rarely access the latter, just check how our data is stored there, but we mention the connection string). I’ll try to write concretely and concisely, and if there are incomprehensible moments, I ask for the article Tutorials on FluentNHibernate with ASP.NET MVC and SQL Server. Part 1where everything is described in more detail. So, let's get started, start Visual Studio and:
    1. Create a new project File-> New-> Project .
    2. We select ASP.NET MVC 4 (.Net Framework 4) and call it NHibernateMVC .
    3. In the Models folder, create the NHibernate folder
    4. In the Package Manager Console, write install-package nhibernate (the examples below also work in FluentNhibernate, (verified!): Install-package Fluentnhibernate).


    After installing Nhibernate, the question arises as to which way we will associate (map) the classes from the application with the tables from the database. NHibernate has three types of mapping for us, such as:
    • XML Create a * .hbm.xml mapping file
    • Attributes It is an add-in for xml mapping. Mapping looks like .NET attributes
    • Mapping by code. - instead of xml-files * .cs ConfORM mapping classes are used
    • Fluent Create * .cs fluent mapping classes


    Mapping options.
    1.XML - files

    The very first of the developed mapping.
    Pros:
    + There are many examples on the Internet.
    + All other methods (Attributes and Fluent) are reduced to receiving these xml files.
    Cons:
    - Completely missing intellisense (!).
    - There is no validation at compile time.

    Well then, let's take a look at it.
    After installing NHibernate, add the Nhibernate.cfg.xml file to the Models-> NHibernate folder
    
    			NHibernate.Connection.DriverConnectionProvider
    		
    			NHibernate.Driver.SqlClientDriver
    		
    			Server=...\SQLENTERPRISE; database=NhibernateTutor; Integrated Security=SSPI;
    		
    			NHibernate.Dialect.MsSql2008Dialect
    		

    I work with SQL Server, so I chose SqlClientDriver. If you are working with a different database, then the NHibernate.Driver list can be viewed here NHibernate.Driver
    Since I have SQL Server 2008, I selected MsSql2008Dialect, all the dialects can be viewed here SQL Dialects
    Create a NhibernateTutor database in SQL Server and write the line connectivity. In this article I will not create tables, they will be generated by themselves, NHibernate.

    Next, add the Book.cs class to the Models folder.
    public class Book {
    		public virtual int Id { get; set; }
    		public virtual string Name { get; set; }
    		public virtual string Description { get; set; }
    }
    

    (PS All its fields must be virtual - this requirement is necessary for lazy load (lazy loading) and for NHibnerate to track all changes in objects.)

    After creating the class, it's time to create a mapping for it. To do this, create an xml-file “Book.hbm.xml” in the Models-> NHibernate folder. ( Caution .
    1. All mapping files must have the * .hbm.xml extension.
    2. Go to the properties of this file (right-click on Book.hbm.xml -> Properties), and in the “Advanced” list change the Build Action property to Embedded Resource . The same thing will need to be done again for other * .hbm.xml files. If you do not specify it, then in the line configuration.AddAssembly (typeof (Book) .Assembly); class NhibernateHelper (which will be created later) there will be an error that it is impossible for the class Book to find its mapping file.)



    What should I pay attention to here. First, assembly and namespace must be the same as the Book class. I'd done because I don’t like guid, and it determines which type (identity, sequence or hilo) to use, depending on the capabilities of the database (Sql Server has this identity).
    • A complete list of generators is here by IdGenerator .
    • List of properties of the Class class .
    • The list of Id properties can be read here Id .
    • List of properties Property Property .


    After creating the xml mapping, create the NHibernateHelper.cs class in the root directory.
        public class NHibernateHelper {
    		public static ISession OpenSession() {
    			var configuration = new Configuration();
    			var configurePath = HttpContext.Current.Server.MapPath(@"~\Models\Nhibernate\nhibernate.cfg.xml");
    			configuration.Configure(configurePath);
    			//Если бы не сделали Book.hbm.xml Embedded Resource, то он бы написал ошибку о невозможности найти файл
    			configuration.AddAssembly(typeof(Book).Assembly);
    			ISessionFactory sessionFactory = configuration.BuildSessionFactory();
                            //Позволяет Nhibernate самому создавать в БД таблицу и поля к ним. 
    			new SchemaUpdate(configuration).Execute(true, true);
    			return sessionFactory.OpenSession();
    		}
        }
    

    More information about setting up ISessionConfiguration here ISessionFactory Configuration

    At the end of all these operations, the following files should have been created.


    Let's now see how NHibernate creates a Book table in the database. Create a class HomeController in the Controllers folder and write the following code.
            public ActionResult Index()
            {
                var session = NHibernateHelper.OpenSession();
                return View();
            }
    

    What the View will look like Now we are not interested, let it be empty. We start the application, go to the SQL Server database and (voila!) We see the Book table in the NhibernateTutor database. There you can change the data types as you like (nvarchar (255) do nvarchar (MAX), but not int!). Until we fill it with data, let's first set up the connections (when a one-to-one connection appears, an error will appear that the Mind table does not correspond to the Book table) or fill in the data and then delete it.



    Now let's move on to setting up relationships between tables.

    1.1 Many-to-many relationships

    Book.csAuthor.cs
    private ISet _authors;
    public virtual ISet Authors { 
        get { 
            return _authors ?? (_authors = new HashSet()); 
        } 
        set { _author = value; }
     }
    

    private ISet _books;
    public virtual ISet Books{
        get { 
            return _books?? (_books= new HashSet()); 
        } 
        set { _books= value; }
    }
    

    Book.hbm.xmlAuthor.hbm.xml
    
    ...........................................
    

    
    ...........................................
    


    Let's take a look at the xml-mapping of these classes. Let's start with the set tag, it is used for the .NET ISet. You can read more about what tags are for collections here Collection of Values , and below I will provide a table of collections and what tags apply to them.
    .Net CollectionMapping
    IEnumerable / ICollection / IListbag
    IList with orderlist
    Isetset
    IDictionarymap


    • table = "Book_Author" is an attribute that creates the Book_Author staging table needed for a many-to-many relationship.
    • name = "Books / Authors" - indicates the name of the collection
    • cascade = "save-update" - indicates that when saving and updating, tables associated with it are also saved and updated. Views cascade = "all | none | save-update | delete | all-delete-orphan", in more detail here LifeCycles and object graphs
    • inverse = “true” means that the opposite Book table is the parent (is responsible for the relationship) and will be updated. More details can be found here inverse true example
    • key column = “BookId” - indicates that the Book table will be associated with the Book_Author table by the BookId key
    • many-to-many class = "Author" column = "AuthorId" - indicates that the Book table will be associated with the Author table by the key "AuthorId"


    Many-to-One (One-to-Many)
    Book.csSeries.cs
    public virtual Series Series { get; set; }
    

    private IList _books;
    public virtual IList Books {
    	get {
    		return _books ?? (_books = new List());
    	}
    	set { _books = value; }
    }
    

    Book.hbm.xml
    Series.hbm.xml



    So, what can I say? We used the tag "bag" since we have IList, column = "Series_id" creates the Series_Id column in the Book table, the rest was said above.

    One to one
    Book.csMind.cs
    private Mind _mind;
    public virtual Mind Mind { 
        get { return _mind ?? (_mind = new Mind()); } 
        set { _mind = value; } 
    }
    
    public virtual Book Book { get; set; }
    
    Book.hbm.xmlMind.hbm.xml


    And here it’s already interesting! constrained = “true” means that for each record in the Book table there must be a record in the Mind table, that is, the Id of the Book table must be equal to the Id of the Mind table. If you try to save the Book object, forgetting about the Mind table, Nhibernate will throw an exception that it cannot save data. That is, you first need to create a Mind object for the Book object. Constantly creating a Mind object is very tedious, so when I create a Book object when I save it, it initializes the Mind object with the code below, and I always have time to fill out the Mind table.
    private Mind _mind;
    public virtual Mind Mind { 
        get { return _mind ?? (_mind = new Mind()); } 
        set { _mind = value; } 
    }
    

    Cascade = “All” When saving, changing, deleting a table, the Book table is also saved, changed, and the Mind table is deleted. So, we created all the connections, it's time to check them by saving, editing or deleting the data. Details under the spoiler below.

    Mapping Performance Testing: CRUD Operations
    Let's create a test application that will save data in the database, update and delete it by changing the HomeController as follows (We comment on unnecessary sections of code):
    public ActionResult Index()
    {
    	using (ISession session = NHibernateHelper.OpenSession()) {
    		using (ITransaction transaction = session.BeginTransaction()) {
    			//Создать, добавить
    			var createBook = new Book();
    			createBook.Name = "Metro2033";
    			createBook.Description = "Постапокалипсическая мистика";
    			createBook.Authors.Add(new Author { Name = "Глуховский" });
    			createBook.Series = new Series { Name = "Метро" };
    			createBook.Mind = new Mind { MyMind = "Постапокалипсическая мистика" };
    			session.SaveOrUpdate(createBook);
    			//Обновить (По идентификатору)
    			var updateBook = session.Get(1);
    			updateBook.Name = "Metro2033";
    			updateBook.Description = "Постапокалипсическая мистика";
    			updateBook.Authors.ElementAt(0).Name = "Петров";
    			updateBook.Series.Name = "Метроном";
    			updateBook.Mind.MyMind = "11111";
    			session.SaveOrUpdate(updateBook);
    			//Удаление (По идентификатору)
    			var deleteBook = session.Get(1);
    			session.Delete(deleteBook);
    			transaction.Commit();
    		}
    		var criteria = session.CreateCriteria();
    		criteria.CreateAlias("Series", "series", JoinType.LeftOuterJoin);
    		criteria.CreateAlias("Authors", "author", JoinType.LeftOuterJoin);
    		criteria.CreateAlias("Mind", "mind", JoinType.LeftOuterJoin);
    		criteria.SetResultTransformer(new DistinctRootEntityResultTransformer());
    		var books = criteria.List();
    		return View(books);
    	}
    }
    

    and change the representation as follows:
    @model IEnumerable
    @{    Layout = null; }
    
    
    
    	Index

    @Html.ActionLink("Create New", "Create")

    @foreach (var item in Model) { @{string strSeries = item.Series != null ? item.Series.Name : null;} }
    @Html.DisplayNameFor(model => model.Name)@Html.DisplayNameFor(model => model.Mind)@Html.DisplayNameFor(model => model.Series)@Html.DisplayNameFor(model => model.Authors)Операции
    @Html.DisplayFor(modelItem => item.Name)@Html.DisplayFor(modelItem => item.Mind.MyMind)@Html.DisplayFor(modelItem => strSeries) @foreach (var author in item.Authors) { string strAuthor = author != null ? author.Name : null; @Html.DisplayFor(modelItem => strAuthor)
    }
    @Html.ActionLink("Edit", "Edit", new { id = item.Id }) | @Html.ActionLink("Details", "Details", new { id = item.Id }) | @Html.ActionLink("Delete", "Delete", new { id = item.Id })

    I think you can do it yourself by creating the appropriate fields for Author, Mind and Series. Having checked all the operations in turn, we will notice that:
    • During Create and Update operations, all data associated with the Book table is updated (remove Cascade = "save-update" or cascade = "all" and the associated data will not be saved)
    • When deleting data from the tables Book, Mind, Book_Author is deleted, and the rest of the data is not deleted, because they have Cascade = "save-update"
    • When deleting data from the tables Book, Mind, Book_Author is deleted, and the rest of the data is not deleted, because they have Cascade = "save-update"

    Further, you can use this code to check the links in the other mapping options, because Criteria works in both Attributes and Fluent. And do not forget to check the keys in the database, you never know what mistake you made, which you will learn about later. For example, the Book table refers by key to the AuthorId of the Book_Author table, instead of the BookId key.


    Detailed information can be found here.
    NHibernate Reference Documentation

    2. ATTRIBUTES
    Is an add-in to Nhibernate.
    Pros:
    + No need to create separate files (* .hbm.xml), write attributes immediately above the fields of the class, that is, entities and mappings are nearby.
    + Intellisense 50/50 support (!). There are tips for writing attributes (such as Name), but not for its properties, which are presented as a string.
    + Easy to switch from xml files to Attributes.
    Cons:
    - The readability of the code is getting worse.
    - Lack of validation at compile time.
    - For properties consisting of more than 1 attribute, indexes should be registered.

    В интернете материалов по NHibernate.Mapping.Attributes мало, даже на сайте nhibernate.info ему отведена всего ОДНА глава(!) NHibernate.Mapping.Attributes и этому есть простое объяснение: он является дополнением (Add-in) к NHibernate, и поэтому фактически имеет такие же настройки, как Nhibernate *.hbm.xml-файлы. Таким образом, вы используете Attributes вместо nasty *.hbm.xml-файлов. Поэтому, если вы используете Атрибуты и у вас возникли проблемы, смело применяйте решения, которые применимы для *.hbm.xml-файлов, благо синтаксис у них одинаков, разобраться не составит труда.
    1. Прежде чем использовать аттрибуты, удалим вначале все маппинг (*.hbm.xml) файлы, они нам больше не понадобятся. (Nhibernate.cfg.xml оставляем!)
    2. To work with the attributes we need NHibernate.Mapping.Attribute.dll, install it through the Package Manager Console , where we will write Install-Package NHibernate.Mapping.Attributes .
    3. Change the NHibernateHelper class as follows

          public class NHibernateHelper {
    		public static ISession OpenSession() {
    			var configuration = new Configuration();
    			var configurePath  = HttpContext.Current.Server.MapPath(@"~\Models\Nhibernate\nhibernate.cfg.xml");
    			configuration.Configure(configurePath);
    //configuration.AddAssembly(typeof(Book).Assembly); заменяем вот на этот код\\
    			HbmSerializer.Default.Validate = true;
    			var stream = HbmSerializer.Default.Serialize(Assembly.GetAssembly(typeof(Book)));
    			configuration.AddInputStream(stream);
    //*****************************************************************************************************\\
    			ISessionFactory sessionFactory = configuration.BuildSessionFactory();
    //Позволяет Nhibernate самому создавать в БД таблицу и поля к ним. 
    			new SchemaUpdate(configuration).Execute(true, true);
    			return sessionFactory.OpenSession();
    		}
    	}
    

    • configurePath -Specify the path to configure the Nhibernate file
    • HbmSerializer.Default.Validate - enables or disables the verification of generated xml streams
    • HbmSerializer.Default.Serialize - serializes classes to xml files

    Now is the time to write Attributes. Change the Book class by adding attributes there as follows
    	[Class]
    	public class Book {
    		[Id(0, Name = "Id")]
    		[Generator(1, Class = "native")]
    		public virtual int Id { get; set; }
    		[Property]
    		public virtual string Name { get; set; }
    		[Property]
    		public virtual string Description { get; set; }
    }
    

    What should be noted? First, there must be attributes on each property that you want to map. Second, did you pay attention to the indices Id (0 ...) and Generator (1 ...)? Indexes must be applied to properties that consist of more than one attribute. This is due to the fact that NHMA generates * .hbm.xml files on the fly from attributes, and it must know in which order to write out xml elements. (Unfortunately, the order of the attribute is not supported using reflection).
    We will delete the Book table from the database (you can not delete it, this is for verification.) Run the project, and if there was no Book table in the database, then it will be created.
    I will not write about relationships, since the syntax is the same as that of * .hbm.xml files, the only difference is that for collections you need to register indexes.

    2.1 Relationships (In Tables)
    Many-to-Many
    Book.csAuthor.cs
    [Set(0, Table = "Book_Author", Cascade = "save-update")]
    [Key(1, Column = "BookId")]
    [ManyToMany(2, ClassType = typeof(Author), Column = "AuthorId")]
    private ISet _authors;
    public virtual ISet Authors { 
    get { 
      return _authors ?? (_authors = new HashSet());
    } 
    set { _author = value; }
     }
    

    [Set(0, Table = "Book_Author", Inverse = true, Cascade = "save-update")]
    [Key(1, Column = "AuthorId")]
    [ManyToMany(2, ClassType = typeof(Book), Column = "BookId")]
    private ISet _books;
    public virtual ISet Books{
        get { 
        return _books?? (_books= new HashSet()); 
        } 
        set { _books= value; }
    }
    


    Many-to-One, One-to-Many
    Book.csSeries.cs
    [ManyToOne(Name = "Series", Column = "SeriesId", 
    ClassType = typeof(Series), Cascade = "save-update")]
    public virtual Series Series { get; set; }
    

    [Bag(0, Name = "Books", Inverse = true)]
    [Key(1, Column = "SeriesId")]
    [OneToMany(2, ClassType = typeof(Book))]
    private IList _books;
    public virtual IList Books{
        get { 
            return _books?? (_books= new List()); 
        } 
        set { _books= value; }
    }
    


    One to one
    Book.csMind.cs
    [OneToOne(Name = "Mind", ClassType = typeof(Mind), 
    Constrained = true, Cascade = "All")]
    private Mind _mind;
    public virtual Mind Mind { 
        get { return _mind ?? (_mind = new Mind()); } 
        set { _mind = value; }
     }
    

    [OneToOne(Name = "Book", ClassType = typeof(Book))]
    public virtual Book Book { get; set; }
    



    3. Mapping ByCode
    Pros:
    + No additional libraries are required (as is the case with attributes)
    + Intellisense 100 support (!).
    + * .Hbm.xml files and Nhibernate.cfg.xml are not required
    + We took the best from Fluent-Nhibernate (lambda expressions) and made the syntax for * .hbm.xml files.
    Cons:
    - Removed the cascade Save-Update property (you can use Cascade.Persist, but still).
    - The structure (in particular, the relationship between classes) does not exactly correspond to the elements of * .hbm.xml files.

    Will be updated ...
    1. Delete the nhibernate.cfg.xml file
    2. Change the NHibernateHelper class as follows

    	public class NHibernateHelper {
    		public static ISession OpenSession() {
    			var cfg = new Configuration()
    			.DataBaseIntegration(db => {
    				db.ConnectionString = @"Server=..\SQLENTERPRISE;initial catalog=NhibernateTutor;Integrated Security=SSPI;";
    				db.Dialect();
    			});
    			var mapper = new ModelMapper();
    			mapper.AddMappings(Assembly.GetExecutingAssembly().GetExportedTypes());
    			HbmMapping mapping = mapper.CompileMappingForAllExplicitlyAddedEntities();
    			cfg.AddMapping(mapping);
    			new SchemaUpdate(cfg).Execute(true, true);
    			ISessionFactory sessionFactory = cfg.BuildSessionFactory();
    			return sessionFactory.OpenSession();
    		}
    	}
    


    Have you noticed a trend? Each method removes one of the files for Nhibernate. In the xml files there were * .hbm.xml files and a nhibernate.cfg.xml file, Attributes no longer needed * .hbm.xml files, in Mapping byCode nhibernate.cfg.xml was no longer needed. It is interesting that it will be removed in the new method (and will it even be at all?).

    The Book class and its mapping should look like this.
    	public class Book {
    		public virtual int Id { get; set; }
    		public virtual string Name { get; set; }
    		public virtual string Description { get; set; }
    	}
    	public class BookMap : ClassMapping {
    		public BookMap() {
    			Id(x => x.Id, map =>  map.Generator(Generators.Native));
    			Property(x=>x.Name);
    			Property(x=>x.Description);
    	} 
    


    3.1 Relationships (In Tables)
    Many-to-Many
    Book.csAuthor.cs
    private ISet _authors;
    public virtual ISet Authors { 
        get { 
            return _authors ?? (_authors = new HashSet()); 
        } 
        set { _author = value; }
     }
    

    private ISet _books;
    public virtual ISet Books{
        get { 
            return _books?? (_books= new HashSet()); 
        } 
        set { _books= value; }
    }
    

    BookMap.csAuthorMap.cs
    Set(a => a.Authors, 
    c => { c.Cascade(Cascade.Persist); 
    c.Key(k => k.Column("BookId")); 
    c.Table("Book_Author");},
    r=>r.ManyToMany(m=>m.Column("AuthorId")));
    

    Set(a => a.Books, 
    c => { c.Cascade(Cascade.All); 
    c.Key(k => k.Column("AuthorId")); 
    c.Table("Book_Author"); c.Inverse(true); },
    r => r.ManyToMany(m => m.Column("BookId")));
    


    Many-to-One, One-to-Many
    Book.csSeries.cs
    public virtual Series Series { get; set; }
    

    private IList _books;
    public virtual IList Books{
        get { 
            return _books?? (_books= new List()); 
        } 
        set { _books= value; }
    }
    

    BookMap.csSeriesMap.cs
    ManyToOne(x => x.Series, 
    c => { c.Cascade(Cascade.Persist); 
    c.Column("Series_Id"); });
    

    Bag(x => x.Books, 
    c => { c.Key(k => k.Column("Series_Id")); c.Inverse(true); }, 
    r =>  r.OneToMany());
    



    One to one
    Book.csMind.cs
    private Mind _mind;
    public virtual Mind Mind { 
    get { return _mind ?? (_mind = new Mind()); } 
    set { _mind = value; } 
    }
    

    public virtual Book Book { get; set; }
    

    BookMap.csMindmap.cs
    OneToOne(x=>x.Mind, c=>{c.Cascade(Cascade.All); 
    c.Constrained(true);});
    

    OneToOne(x=>x.Book,
    c=>c.Cascade(Cascade.None));
    



    Details here StackOverflow Mapping-By-Code

    4. FLUENT
    It offers an alternative to standard NHibernate XML display files. Instead of writing XML files, you write mappings in strongly typed C # code (via lambda expressions). Thanks to this, there is refactoring; the readability and ease of writing code are improved.
    Pros:
    + 100% intellisense support!
    + Excellent documentation Fluent-Nhibernate
    + Validation during compilation
    + No need to create a file Nhibernate.cfg.xml, all settings, including the connection string, can be written in NhibernateHelper.
    Cons:
    - Compared to other versions of NHibernate, a slightly unusual mapping syntax is used.

    Since FluentNhibernate and Nhibernate are slightly different, let me write as if I were re-creating the application? So, let's start, create a new application.
    1. In the Package Manager Console, write install-package fluentnhibernate
    2. In the “Models” folder, create the “Book.cs” class (Models-> Book.cs)
    3. Add the NHibernate folder to Models and create the NHibernateHelper.cs class (Models-> NHibernate-> NHibernateHelper.cs)

    public class NHibernateHelper {
        public static ISession OpenSession() {
           ISessionFactory sessionFactory = Fluently.Configure()
    .Database(MsSqlConfiguration.MsSql2008.ConnectionString(@"Server=..\SQLENTERPRISE; initial catalog= Biblioteca; Integrated Security=SSPI;").ShowSql()
           ) 
           .Mappings(m =>m.FluentMappings.AddFromAssemblyOf()) 
           .ExposeConfiguration(cfg => new SchemaUpdate(cfg).Execute(false, true)) 
           .BuildSessionFactory(); 
           return sessionFactory.OpenSession();
        }
    }
    


    In this NhibernateHelper.cs it should be noted that now we write the connection string to the database here. And yes, there are lambda expressions.

    Fill the Book.cs class
    public class Book {
            public virtual int Id { get; set; }
            public virtual string Name { get; set; }
            public virtual string Description { get; set; }
    }
    

    and create a mapping class for it
           public class BookMap : ClassMap {
                 public BookMap() {
                        Id(x => x.Id);
                        Map(x => x.Name);
                        Map(x => x.Description);
                 }
           }
    

    Next, create the HomeController class in the Controllers folder and write the following code.
    public ActionResult Index()
    {
         var session = NHibernateHelper.OpenSession();
         return View();
    }
    

    Создайте любую View, и после запуска приложения в SQL Server будет создана таблица Book.

    4.1 Отношения (В таблицах)
    Многие-ко-многим
    Book.csAuthor.cs
    private ISet _authors;
    public virtual ISet Authors { 
        get { 
            return _authors ?? (_authors = new HashSet()); 
        } 
        set { _author = value; }
     }
    

    private ISet _books;
    public virtual ISet Books{
        get { 
            return _books?? (_books= new HashSet()); 
        } 
        set { _books= value; }
    }
    

    BookMap.csAuthorMap.cs
    HasManyToMany(x => x.Authors)
    	.Cascade.SaveUpdate()
    	.Table("Book_Author");
    

    HasManyToMany(x => x.Books)
    	.Cascade.All()
    	.Inverse().Table("Book_Author");
    


    Многие-к-одному, один-ко-многим
    Book.csSeries.cs
    public virtual Series Series { get; set; }
    

    private IList _books;
    public virtual IList Books{
        get { 
            return _books?? (_books= new List()); 
        } 
        set { _books= value; }
    }
    

    BookMap.csSeriesMap.cs
    References(x => x.Series).Cascade.SaveUpdate();
    

    HasMany(x => x.Books)
    	.Inverse();
    


    Метод References применяется на стороне «Многие-к-одному», на другой стороне «Один-ко-многим» будет метод HasMany.

    Один-к-одному
    Book.csMind.cs
    private Mind _mind;
    public virtual Mind Mind { 
    get { return _mind ?? (_mind = new Mind()); } 
    set { _mind = value; } 
    }
    

    public virtual Book Book { get; set; }
    

    BookMap.csMindMap.cs
    HasOne(x => x.Mind).Cascade.All().Constrained();
    

    HasOne(x => x.Book);
    


    Метод .Constrained() говорит NHibernate, что для записи из таблицы Book должна соответствовать запись из таблицы Mind (id таблицы Mind должен быть равен id таблицы Book)

    Варианты запросов (NHibernate Queries)

    NHibernate обладает мощными инструментами для создания запросов, такие как.
    • Методы session.Get и session.Load. Получение (списка) объекта(ов) по первичному ключу.
    • SQL (метод CreateSQLQuery) — в основном используется для преобразования SQL-скрипта к определенному классу
    • HQL — это SQL-подобный язык, хорошо подходит для статических запросов.
    • LINQ to NHibernate — В NHibernate 2x linq-запросы задавались интерфейсом ISession.Linq, который хорошо работал для простых запросов. C выходом Nhibernate 3 (интерфейс изменился на ISession.Query) получил более широкие возможности, хотя и не получил широкое распространение среди программистов
    • Criteria (Часто используемый) — хорошо подходит для построения динамических запросов, свойства задаются в виде строки.
    • QueryOver (Часто используемый) — Впервые появился в версии NHibernate 3. Также хорошо подходит для построения динамический запросов, поддерживается intellisense


    Запрос по идентификатору
    //Выбор объекта по иденитфикатору, (методы Get и Load)
    var book = session.Get(1);
    

    Метод SQLCreateQuery
    //SQl-запрос
    var queryBook = string.Format(@"select Id, Name, Description from Book");
    //Вывод списка всех книг
    var books = session.CreateSQLQuery(queryBook)
    //SQl-запрос ассоциирует с классом
    .SetResultTransformer(Transformers.AliasToBean(typeof(Book)))
    .List().ToList();
    

    Lists with restrictions
    //hql
    var hqlQuery = session.CreateQuery("from Book b where b.Series.Id = ? order by p.Id")
    	//Указывает 0 параметру значение int=2
    	.SetInt32(0,2);
    var books = hqlQuery.List();
    //NHibernate.Linq (session.Linq in NH 2/session.Query in NH3)
    var linq = (from book in session.Query()
    			where book.Series.Id == 2
    			orderby book.Id
    			select book);
    var books = linq.ToList();
    return View(books);
    //criteria
    var criteria = session.CreateCriteria()
    	//"Restrictions" используется как "Expression"
    	.Add(Restrictions.Eq("Series.Id", 3))
    	//Order
    	.AddOrder(Order.Asc("Id"));
    var books = criteria.List();
    //query over
    var queryOver = session.QueryOver()
    	.Where(x => x.Series.Id == 2)
    	.OrderBy(x => x.Id).Asc;
    var books = queryOver.List();
    return View(books);
    


    I wanted to make similar queries on NHibernate Query, such as Join, Projecting, and others, but it turned out that there are already excellent articles about it from NHibernate Queries and NHibernate More Queries.

    If you are interested in QueryOver, you can see the types of queries here QueryOver in NH3

    Article fits towards the end, and I wanted to finish it with the following material.

    Lazy and eager-loading (Lazy and Greedy downloads)
    Lazy loading and eager loading are the methods that NHibernate uses to load the necessary data into the entity’s navigation properties
    -> Lazyloading - lazy loading. The first time you access the entity (Book), the corresponding related data is not loaded. However, the first time you access the navigation property (Book.Genres), the associated data is loaded automatically. At the same time, many queries are made to the database: one for the entity and one each time the data is downloaded.

    //Код в представлении (View)
    @foreach (var item in Model) { //Загружены ТОЛЬКО все записи таблицы Book
            @foreach (var genre in item.Genres) { //Теперь загрузились поля Genres, связанные с таблицей Book 
    		@Html.DisplayFor(modelItem => genre.Name) 
    } }

    -> Eagerloading - greedy download . Data is loaded when accessing the entity. This is usually followed by a join request that returns all the data.
    //Код в представлении (View)
    @foreach (var item in Model) { //Загружены ВСЕ записи таблицы Book и связанных с ним таблиц.
            @foreach (var genre in item.Genres) { 
    		@Html.DisplayFor(modelItem => genre.Name) 
    } }


    NHibernate uses Fetch strategies for greedy downloads.

    Let's look at EagerLoading and LazyLoading downloads, using Query Over as an example.
    --- Query Over - Lazyloading download code ---
        using (ISession session = NHibernateHelper.OpenSession()) {
           Genre genreAl = null; Author authorAl = null; Series seriesAl = null;
    	    var books = session.QueryOver()
                            //Left Join с таблицей Genres 
    			.JoinAlias(p => p.Authors, () => authorAl, JoinType.LeftOuterJoin)
    			.JoinAlias(p => p.Series, () => seriesAl, JoinType.LeftOuterJoin)
                            //Убирает повторяющиеся id номера таблицы Book.
    			.TransformUsing(Transformers.DistinctRootEntity).List();
    	  return View(books);
        }
    

    --- Query Over - EagerLoading download code ---
    using (ISession session = NHibernateHelper.OpenSession()) {
    var books = session.QueryOver()
    .Fetch(a => a.Authors).Eager.Fetch(s => s.Series).Eager
    .TransformUsing(Transformers.DistinctRootEntity).List();
    return View(books);
    

    as you can see, we did not use LeftJoin as in the previous example, because Fetch immediately binds all the data to the Book object.

    You can read about Fecth strategy here Nhibernate Fetch strategy and about mapping using lazy or eager loading - here Attributes lazy, fetch and batch-size . There is also an article on NhibernateInfo - lazy-eager loading The

    article has come to an end, thanks for your attention.

    Also popular now: