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); } }
少し説明
- var books = session.QueryOver <Book>() -SQLスクリプトの実行のように: Select * From Book ;
- .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 - .TransformUsing(Transformers.DistinctRootEntity)-SQLスクリプトの実行のように: SELECT distinct Book.Id ... 、(同じidの重複エントリを削除します)
関連付けの種類
.JoinAlias(p => p.Genres、()=> genreAl、JoinType.LeftOuterJoin)
- LeftOuterJoin-左側のテーブル( Book )からすべてのレコードを選択し、右側のテーブル( ジャンル )のレコードをそれらに添付します。 右側のテーブルに対応するレコードが見つからない場合は、Nullとして表示します
- RightOuterJoinはLEFT JOINとは異なり、右のテーブル( ジャンル )からすべてのエントリを選択し、左のテーブル( ブック )のエントリをそれらに添付します。
- 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>
すべての操作を順番に確認すると、次のことがわかります。
- 作成および更新操作中に、Bookテーブルに関連付けられたすべてのデータが更新されます(Cascade = "save-update"またはcascade = "all"を削除すると、関連付けられたデータは保存されません)
- テーブルBook、Mind、Book_Authorからデータを削除すると、Cascade = "save-update"があるため、残りのデータは削除されません。
- テーブルBook、Mind、Book_Authorからデータを削除すると、Cascade = "save-update"があるため、残りのデータは削除されません。
継承を持つクラスのマッピング。
そして、継承を持つクラスをどのようにマップしますか? 例があるとします:
// 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データベースに次の(空の)テーブルが表示されます
