パート2.クラスの作成、データベースへのマッピングおよび入力

クラスとマッピング

ASP.NET MVCおよびSQL Serverを使用したFluentNHibernateのチュートリアル。 パート1
パート3.テーブルからのデータの表示(操作リスト)

前のパートでは、接続のタイプ(1対1、1対多、多対多)、および1つのクラスBookとそのマッピングクラスBookMapを調べました。 第2部では、サブタイトル1.3.1リンクの上にあるデータベース図の前の章で示したように、Bookクラスを更新し、残りのクラスとそれらの間のリンクを作成します。


クラスとマッピングコード(コメント付き)
クラスブック

public class Book { //  public virtual int Id { get; set; } // public virtual string Name { get; set; } // public virtual string Description { get; set; } //   public virtual int MfRaiting { get; set; } //  public virtual int PageNumber { get; set; } //   public virtual string Image { get; set; } //   (  !) public virtual DateTime IncomeDate { get; set; } // (--) // ISet   IList?    (IList)     JOIN ,        JOIN,       ISet public virtual ISet<Genre> Genres { get; set; } // (--) public virtual Series Series { get; set; } //   (--) private Mind _mind; public virtual Mind Mind { get { return _mind ?? (_mind = new Mind()); } set { _mind = value; } } // (--) public virtual ISet<Author> Authors { get; set; } // ,   null  . public Book() { //  (       --  ,     ,   ) Genres = new HashSet<Genre>(); Authors = new HashSet<Author>(); } } //  Book public class BookMap : ClassMap<Book> { public BookMap() { Id(x => x.Id); Map(x => x.Name); Map(x => x.Description); Map(x => x.MfRaiting); Map(x => x.PageNumber); Map(x => x.Image); Map(x => x.IncomeDate); // -- HasManyToMany(x => x.Genres) //  All -   ,   ,   ////    .Cascade.SaveUpdate() //         Genre! .Table("Book_Genre"); HasManyToMany(x => x.Authors) .Cascade.SaveUpdate() .Table("Book_Author"); //    References(x => x.Series); // --.  . HasOne(x => x.Mind).Cascade.All().Constrained(); } } 


クラス著者

  public class Author { public virtual int Id { get; set; } //- public virtual string Name { get; set; } // public virtual string Biography { get; set; } // public virtual ISet<Book> Books { get; set; } //  public Author() { Books=new HashSet<Book>(); } } //  public class AuthorMap : ClassMap<Author> { public AuthorMap() { Id(x => x.Id); Map(x => x.Name); Map(x => x.Biography); // -- HasManyToMany(x => x.Books) //  All -   ,   ,   //    .Cascade.All() //  .    (Book)     . .Inverse() //         Book! .Table("Book_Author"); } } 


クラスのジャンル

  public class Genre { public virtual int Id { get; set; } //  public virtual string Name { get; set; } //   public virtual string EngName { get; set; } // public virtual ISet<Book> Books { get; set; } //  public Genre() { Books=new HashSet<Book>(); } } //  public class GenreMap : ClassMap<Genre> { public GenreMap() { Id(x => x.Id); Map(x => x.Name); Map(x => x.EngName); // -- HasManyToMany(x => x.Books) //  All -   ,   ,   //    .Cascade.All() //  .    (Book)     . .Inverse() //         Book! .Table("Book_Genre"); } } 


クラスの意見:

  public class Mind { public virtual int Id { get; set; } //  public virtual string MyMind { get; set; } //  public virtual string MindFantLab { get; set; } // public virtual Book Book { get; set; } } // ind public class MindMap:ClassMap<Mind> { public MindMap() { Id(x => x.Id); Map(x => x.MyMind); Map(x => x.MindFantLab); //    HasOne(x => x.Book); } } 


クラスサイクル(シリーズ):

  public class Series { public virtual int Id { get; set; } public virtual string Name { get; set; } //  IList,   ISet,    Book, Series      ,     ISet public virtual IList<Book> Books { get; set; } // . public Series() { Books = new List<Book>(); } } public class SeriesMap : ClassMap<Series> { public SeriesMap() { Id(x => x.Id); Map(x => x.Name); // -- HasMany(x => x.Books) ////  .    (Book)     . .Inverse() } } 



少し説明
パブリック仮想ISet <Genre>ジャンル{get; セット; }
パブリック仮想ISet <Author> Authors {get; セット; }

たとえば、多くのIList <Class>に馴染みのないISet <Class>がなぜですか ISetの代わりにIListを使用してプロジェクトを開始しようとしても、大きな違いはありません(テーブルとクラスが作成されます)。 ただし、LeftJoinsのBookクラスにアクセスすると、同時にジャンルと著者のテーブルが表示され、ビュー内のBookテーブル(Distinct Book.Id)から繰り返しないエントリを表示しようとすると、Nhibernateは例外とエラーをスローします。
複数のバッグを同時に取得することはできません。
そのような場合、特にセットはこれを対象としているため、ISetを使用します(重複エントリは無視されます)。

多対多の関係。

作者
HasManyToMany(x => x.Genres)
.Cascade.SaveUpdate()
.Table( "Book_Author");
HasManyToMany(x => x.Books)
.Cascade.All()
.Inverse()
.Table( "Book_Author");

NHibernateには「メイン」テーブルの概念があります。 「Book」テーブルと「Author」テーブルの多対多の関係は同じですが(著者は多くの本を持ち、本は多くの著者を持つことができます)、Nhibernateはプログラマーに2番目に保存されるテーブルを指定することを要求します(メソッドがあります)。つまり、最初にエントリがBookテーブルで作成、更新、削除され、その後Authorテーブルでのみ作成されます。
Cascade.Allは、保存、更新、削除でカスケード操作を実行することを意味します。 つまり、オブジェクトが保存、更新、または削除されると、すべての依存オブジェクトがチェックされ、作成/更新/追加されます(Ps。Cascade.All-> .Cascade.SaveUpdate()を登録できます。代わりにCascade.Delete())
メソッド.Table( "Book_Author"); データベースに「中間」テーブル「Book_Author」を作成します。

多対1、1対多の関係。
シリーズ
参照(x => x.Series).Cascade.SaveUpdate();
HasMany(x => x.Books)
.Inverse();

Referencesメソッドは多対1側に適用され、HasManyメソッドは他の1対多側に適用されます。

1対1の関係
HasOne(x => x.Mind).Cascade.All()。Constrained();
HasOne(x => x.Book);

.Constrained()メソッドは、NHibernateにBookテーブルのエントリについて、Mindテーブルのエントリが一致する必要があることを伝えます(MindテーブルのIDはBookテーブルのIDと等しくなければなりません)

ここでプロジェクトを開始してBibiliotecaデータベースを見ると、すでに形成された関係を持つ新しいテーブルが表示されます。

次に、作成したテーブルにデータを入力します...
これを行うには、データベースにデータを保存するテストアプリケーションを作成し、HomeControllerを次のように変更して更新および削除します(コードの不要な部分についてコメントします)。
 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.Genres.Add(new Genre { Name = " " }); createBook.Series = new Series { Name = "" }; createBook.Mind = new Mind { MyMind = " " }; session.SaveOrUpdate(createBook); // ( ) //var series = session.Get<Series>(1); //var updateBook = session.Get<Book>(1); //updateBook.Name = "Metro2034"; //updateBook.Description = ""; //updateBook.Authors.ElementAt(0).Name = ""; //updateBook.Genres.ElementAt(0).Name = ""; //updateBook.Series = series; //updateBook.Mind.MyMind = "11111"; //session.SaveOrUpdate(updateBook); // ( ) //var deleteBook = session.Get<Book>(1); //session.Delete(deleteBook); transaction.Commit(); } Genre genreAl = null; Author authorAl = null; Series seriesAl = null; Mind mindAl = null; var books = session.QueryOver<Book>() //Left Join   Genres .JoinAlias(p => p.Genres, () => genreAl, JoinType.LeftOuterJoin) .JoinAlias(p => p.Authors, () => authorAl, JoinType.LeftOuterJoin) .JoinAlias(p => p.Series, () => seriesAl, JoinType.LeftOuterJoin) .JoinAlias(p => p.Mind, () => mindAl, JoinType.LeftOuterJoin) //  id   Book. .TransformUsing(Transformers.DistinctRootEntity).List(); return View(books); } } 


少し説明
  1. var books = session.QueryOver <Book>() -SQLスクリプトの実行のように: Select * From Book ;
  2. .JoinAlias(p => p.Genres、()=> genreAl、JoinType.LeftOuterJoin)-SQLスクリプトの実行と同様:
    SELECT * FROM Book
    inner JOIN Book_Genre ON book.id = Book_Genre.Book_id
    LEFT JOINジャンルON Book_Genre.Genre_id =ジャンル.id
  3. .TransformUsing(Transformers.DistinctRootEntity)-SQLスクリプトの実行のように: SELECT distinct Book.Id ... 、(同じidの重複エントリを削除します)


関連付けの種類
.JoinAlias(p => p.Genres、()=> genreAl、JoinType.LeftOuterJoin)
  1. LeftOuterJoin-左側のテーブル( Book )からすべてのレコードを選択し、右側のテーブル( ジャンル )のレコードをそれらに添付します。 右側のテーブルに対応するレコードが見つからない場合は、Nullとして表示します
  2. RightOuterJoinはLEFT JOINとは異なり、右のテーブル( ジャンル )からすべてのエントリを選択し、左のテーブル( ブック )のエントリをそれらに添付します。
  3. InnerJoin-右側のテーブル( ジャンル )からの対応するレコードを持つ左側のテーブル( ブック )のレコードのみを選択し、右側のテーブルからのレコードをアタッチします


次のように表現を変更します。
インデックスビュー
 @model IEnumerable<NhibernateMVC.Models.Book> @{ Layout = null; } <!DOCTYPE html> <html> <head> <meta name="viewport" content="width=device-width" /> <title>Index</title> <style> th, td { border: 1px solid; } </style> </head> <body> <p>@Html.ActionLink("Create New", "Create")</p> <table> <tr> <th>@Html.DisplayNameFor(model => model.Name)</th> <th>@Html.DisplayNameFor(model => model.Mind)</th> <th>@Html.DisplayNameFor(model => model.Series)</th> <th>@Html.DisplayNameFor(model => model.Authors)</th> <th>@Html.DisplayNameFor(model => model.Genres)</th> <th></th> </tr> @foreach (var item in Model) { <tr> <td>@Html.DisplayFor(modelItem => item.Name)</td> <td>@Html.DisplayFor(modelItem => item.Mind.MyMind)</td> @{string strSeries = item.Series != null ? item.Series.Name : null;} <td>@Html.DisplayFor(modelItem => strSeries)</td> <td> @foreach (var author in item.Authors) { string strAuthor = author != null ? author.Name : null; @Html.DisplayFor(modelItem => strAuthor) <br /> } </td> <td> @foreach (var genre in item.Genres) { string strGenre = genre!= null ? genre.Name : null; @Html.DisplayFor(modelItem => strGenre) <br /> } </td> <td> @Html.ActionLink("Edit", "Edit", new { id = item.Id }) | @Html.ActionLink("Details", "Details", new { id = item.Id }) | @Html.ActionLink("Delete", "Delete", new { id = item.Id }) </td> </tr> } </table> </body> </html> 


すべての操作を順番に確認すると、次のことがわかります。


継承を持つクラスのマッピング。
そして、継承を持つクラスをどのようにマップしますか? 例があるとします:
  //   public class TwoDShape { // public virtual int Width { get; set; } // public virtual int Height { get; set; } } //  public class Triangle : TwoDShape { //  public virtual int Id { get; set; } //  public virtual string Style { get; set; } } 


原則として、このマッピングには複雑なものはありません;派生クラス、つまりTriangleテーブルに対して1つのマッピングを作成するだけです。
  //  public class TriangleMap : ClassMap<Triangle> { public TriangleMap() { Id(x => x.Id); Map(x => x.Style); Map(x => x.Height); Map(x => x.Width); } } 

アプリケーションを起動すると、Bibliotecaデータベースに次の(空の)テーブルが表示されます

Source: https://habr.com/ru/post/J264961/


All Articles