C# と VB.NET の質問掲示板

ASP.NET、C++/CLI、Java 何でもどうぞ

C# と VB.NET の入門サイト

コードファーストで多対多のテーブルをカスタマイズしたい

[トピック内 14 記事 (1 - 14 表示)]  << 0 >>

■95881 / inTopicNo.1)  コードファーストで多対多のテーブルをカスタマイズしたい
  
□投稿者/ じぇい (1回)-(2020/10/06(Tue) 17:47:00)

分類:[.NET 全般] 

お世話になります。

VisualStudio2019でASP.NET(C#) MVC5+EntityFramework6のWEBアプリを作成しています。
DBはSQLServer2012です。

コードファーストで以下のようなモデルを定義しました。

■Bookモデル
 public int Id { get; set; }
 [Index("IX_BookCode", 1, IsUnique = true)]
 public int BookCode { get; set; }
 public string BookName { get; set; }

 [ForeignKey("BookCode")]
 public virtual ICollection<BookGenre> BookGenres { get; set; }

■BookGenreモデル(中間テーブル)
 public int Id { get; set; }
 public int BookCode { get; set; }
 public int GenreId { get; set; }
 public int SortNo { get; set; }

 [ForeignKey("BookCode")]
 public virtual Book Book { get; set; }
 public virtual Genre Genres { get; set; }


■Genreモデル
 public int Id { get; set; }
 public string GenreName { get; set; }

 public virtual ICollection<BookGenre> BookGenres { get; set; }


書籍(Book)に複数のジャンルをつけたいと思っています。
表示時にはジャンルの表示順を指定したく、BookGenreモデルにはSortNoを定義しています。
上記定義で作成されたテーブルを見るとBookGenre上のBookへのFKはBook.Idに紐づいてしまっています。
Book.BookCodeに紐づけるにはどうしたら良いのでしょうか?
ちなみに出来たテーブルに対して手動での修正は可能でした。

宜しくお願い致します。





引用返信 編集キー/
■95882 / inTopicNo.2)  Re[1]: コードファーストで多対多のテーブルをカスタマイズしたい
□投稿者/ WebSurfer (2107回)-(2020/10/06(Tue) 18:33:43)
No95881 (じぇい さん) に返信

> Book.BookCodeに紐づけるにはどうしたら良いのでしょうか?

BookCode というのが ISBN のようなユニークなものならそれを主キーにしてはいかがですか?
引用返信 編集キー/
■95884 / inTopicNo.3)  Re[2]: コードファーストで多対多のテーブルをカスタマイズしたい
□投稿者/ じぇい (2回)-(2020/10/07(Wed) 10:09:39)
2020/10/07(Wed) 10:10:52 編集(投稿者)
2020/10/07(Wed) 10:10:48 編集(投稿者)

> BookCode というのが ISBN のようなユニークなものならそれを主キーにしてはいかがですか?

そもそもの話になるかもしれませんが、
自分的にも仰る通りBookCode(ナチュラルキー)を主キーにしたほうが分かりやすいのですが、
現行システムの焼き直しで、現行がそのようなDBになっていたので踏襲ということで、このような定義になっています。

また、世の中のWEB系フレームワーク(?)の主流がサロゲートキーを主キーにすることを推奨(?)しているようなのと、
現行はRuby(知りません)ですが、
今回使用するEntity Frameworkも書籍やサイト記事でも基本、IDという自動採番のプロパティが主キーになっているのも
このような定義になった理由の一つです。

どちらが良いか、アプリに合うかは別として、
Entity Frameworkでは自動採番の主キー以外(外部キー)でのリレーションはあまりにもイレギュラーなんでしょうか?
モデルバインドでBooks.BookGenres.Genresとジャンルが取得できたら便利だなと思いやってみたのですが。

まだ定義変更は可能なのでBookCodeを主キーにすることも検討してみますが、影響範囲はそれなりにありそうでして。。。


引用返信 編集キー/
■95886 / inTopicNo.4)  Re[3]: コードファーストで多対多のテーブルをカスタマイズしたい
□投稿者/ WebSurfer (2108回)-(2020/10/07(Wed) 12:10:43)
No95884 (じぇい さん) に返信

そもそもの話であれば、主キー以外に外部キー制約を貼るということはあり得ないのでは?

DB 設計に無知な自分が知らないだけという可能性は否定できませんが、Microdoft のドキュメント、

主キー制約と外部キー制約
https://docs.microsoft.com/ja-jp/sql/relational-databases/tables/primary-and-foreign-key-constraints?view=sql-server-ver15

の Foreign Key Constraints のセクションに以下の説明があります。

"外部キー参照では、1 つのテーブルの主キー値が格納されている列が別のテーブルの 1 つ以上の列
によって参照されたときに、2 つのテーブル間にリンクが作成されます。 この列は、2 番目のテー
ブルの外部キーになります。"

"In a foreign key reference, a link is created between two tables when the column or columns
that hold the primary key value for one table are referenced by the column or columns in
another table. This column becomes a foreign key in the second table."

外部キーを貼る相手は主キーと読めます。

ということで、質問者さんのコードでナビゲーションプロパティに付与した [ForeignKey("BookCode")] は
無視されて、規約通りに FK 制約が貼られ、

> 上記定義で作成されたテーブルを見るとBookGenre上のBookへのFKはBook.Idに紐づいてしまっています。

という結果にになったように見えます。

> また、世の中のWEB系フレームワーク(?)の主流がサロゲートキーを主キーにすることを推奨(?)しているようなのと、
> 今回使用するEntity Frameworkも書籍やサイト記事でも基本、IDという自動採番のプロパティが主キーになっているのも
> このような定義になった理由の一つです。

EF Code First の規約に従ってそういう結果になっただけで、「推奨」ということではないでしょう。

int 型のプロパティで名前が Id とか クラス名Id の場合は EF Code First の規約によって DB の当該フィールドは int
IDENTITY(1,1) 型の主キーになります。そうならないようにする方法はもちろんあって、どのようにしたいかによって何と
でもできるはずです。

どうしたいのですか?

> Entity Frameworkでは自動採番の主キー以外(外部キー)でのリレーションはあまりにもイレギュラーなんでしょうか?

上の述べたように主キー以外に FK を貼るのはあり得ないと思っているのですけど。

> モデルバインドでBooks.BookGenres.Genresとジャンルが取得できたら便利だなと思いやってみたのですが。

意味が分かりません。今のまま、Book.Id に FK を貼っても何の不都合もないと思いますけど。何を考えて
いるのでしょう?

引用返信 編集キー/
■95891 / inTopicNo.5)  Re[4]: コードファーストで多対多のテーブルをカスタマイズしたい
□投稿者/ じぇい (3回)-(2020/10/07(Wed) 19:00:36)
No95886 (WebSurfer さん) に返信

> 主キー制約と外部キー制約
> https://docs.microsoft.com/ja-jp/sql/relational-databases/tables/primary-and-foreign-key-constraints?view=sql-server-ver15

ありがとうございます。確かに外部キーの「相手」は主キーと読めますね。
となると制約をかけたい方が「自分」となり、そちらは必ずしも主キーじゃなくても良いということでしょうか。

結果、以下のように、Book.BookCodeを主キーにし、BookGenreからBookへの参照を削除することで目的は達成できました。
■Bookモデル
 [Key]
 public int BookCode { get; set; }
 public string BookName { get; set; }

 [ForeignKey("BookCode ")]
 public virtual ICollection<BookGenre> BookGenres { get; set; }

■BookGenreモデル(中間テーブル)
 public int Id { get; set; }
 public int BookCode { get; set; }
 public int GenreId { get; set; }
 public int SortNo { get; set; }

 public virtual Genre Genre { get; set; }


■Genreモデル
 public int Id { get; set; }
 public string GenreName { get; set; }



> どうしたいのですか?

やりたいことは、BookモデルをスキャフォールディングしたDetailビュー側で、

@model NktWeb.Models.Book
<p>@Html.DisplayFor(model => model.BookName)</p>
<table>
@foreach (var item in Model.BookGenres)
{
<tr>
<td>
@Html.DisplayFor(modelItem => item.Genres.GenreName)
</td>
</tr>
}
</table>

というようにBookに付与されているGenreNameを表示したかっただけです。
ちょっと外部キー制約という内容とはずれていたかもしれません。
極端な話、外部キーの制約自体はアプリ側でチェックを実装するので設定がなくても良いのですが、
上記のようにビュー側でForeachで1対多の”多”の情報を取得する方法が分からず
外部キーの設定であるナビゲーションプロパティを設定しています。

> 意味が分かりません。今のまま、Book.Id に FK を貼っても何の不都合もないと思いますけど。何を考えて
> いるのでしょう?

あくまでBook.Idは連番の主キー、Book.BookCodeをリレーションのキーにしたいという前提のお話でした。
ですので、仰る通り上記のBookCodeを主キーにする形でも大丈夫ではあります。


経験が浅く、こう、と言ったやり方を持っておらず、模索しながらなのでうまく説明できてないかもしれませんが、
「ビュー側でForeachで1対多の”多”の情報を取得する」
というのが今回の目的です。
ナビゲーションプロパティ設定無し(できれば設定したくないので)で可能なのでしょうか?

引用返信 編集キー/
■95893 / inTopicNo.6)  Re[5]: コードファーストで多対多のテーブルをカスタマイズしたい
□投稿者/ WebSurfer (2109回)-(2020/10/07(Wed) 20:40:52)
No95891 (じぇい さん) に返信

何かしたいのか全然分かりませんが、質問者さんのやりたいことができたのなら
それでいいんじゃないですか・・・としか言えません。

正直言うと、もう少し勉強してもらえないと話が通じないので、これ以上のやり
取りは、その後でお願いしたいです。


引用返信 編集キー/
■95902 / inTopicNo.7)  Re[6]: コードファーストで多対多のテーブルをカスタマイズしたい
□投稿者/ じぇい (4回)-(2020/10/08(Thu) 09:35:29)
No95893 (WebSurfer さん) に返信

失礼しました。
勉強します。
引用返信 編集キー/
■95903 / inTopicNo.8)  Re[5]: コードファーストで多対多のテーブルをカスタマイズしたい
□投稿者/ WebSurfer (2110回)-(2020/10/08(Thu) 09:49:49)
No95891 (じぇい さん) に返信

質問者さんの DB の構造とほぼい同じチュートリアルがあるのを思い出しましたので紹介し
ておきます。

MVC 5 を使用する Entity Framework 6 Code First の概要
https://docs.microsoft.com/ja-jp/aspnet/mvc/overview/getting-started/getting-started-with-ef-using-mvc/

EF Code First で Student ⇔ Enrollmemnt ⇔ Course というテーブルを作り、多対多のリレー
ションを貼るようにしています。以下の画像の通りです(ここには画像は貼れないので自分の
借りているレンタルサーバーにアップしておきました)。

http://surferonwww.info/BlogEngine/image.axd?picture=2020%2f10%2fstudent.jpg

このチュートリアルの最初の 2 つ(下記)を行えば 2 つ目のチュートリアルで Detail を
つくるところで普通はこうするというのが分かるかも。

・Entity Framework データ モデルを作成する
・基本 CRUD 機能を実装する

できれば以下のチュートリアルまでやってみることをお勧めします。特に「関連データを読
み込む方法を学習する」のところ。

・関連データの読み取り
引用返信 編集キー/
■95921 / inTopicNo.9)  Re[6]: コードファーストで多対多のテーブルをカスタマイズしたい
□投稿者/ じぇい (5回)-(2020/10/09(Fri) 13:11:36)
No95903 (WebSurfer さん) に返信

> できれば以下のチュートリアルまでやってみることをお勧めします。特に「関連データを読
> み込む方法を学習する」のところ。
>
> ・関連データの読み取り

チュートリアル、見てはいましたが、理解や経験が足りないのかイレギュラーな内容が追加されると途端に混乱してしまいます。。
試してみて再度考えてみます。
ありがとうございました。
引用返信 編集キー/
■95923 / inTopicNo.10)  Re[7]: コードファーストで多対多のテーブルをカスタマイズしたい
□投稿者/ WebSurfer (2112回)-(2020/10/09(Fri) 13:33:52)
No95921 (じぇい さん) に返信

> 理解や経験が足りないのかイレギュラーな内容が追加されると途端に混乱してしまいます。。

何を「イレギュラーな内容」と言っているのか分かりませんが・・・

私に言わせてもらえれば「イレギュラー」なのは、最初の質問のコードで主キーでない Book.BookCode
に BookGenre から FK 制約を張ろうとしたことぐらいです。

それでも Code First で DB を生成したら Book.Id に FK 制約が張られたということのようですから、
結果はチュートリアルの DB の構造と同じになったわけで、後はチュートリアルの、

・Entity Framework データ モデルを作成する
・基本 CRUD 機能を実装する

に沿って Detail を作ってみればいいのでは? そこまではやったんですか? それもやってなくて
議論はできないですよ。

チュートリアルの、

・関連データの読み取り

は Detail ができてからの話です。
引用返信 編集キー/
■95929 / inTopicNo.11)  Re[8]: コードファーストで多対多のテーブルをカスタマイズしたい
□投稿者/ じぇい (6回)-(2020/10/09(Fri) 16:22:43)
No95923 (WebSurfer さん) に返信

> 何を「イレギュラーな内容」と言っているのか分かりませんが・・・
中間テーブルを自分で定義して使う、というところで分からなくなりました。。
(イレギュラーというよりチュートリアルと違うところと言い換えた方が良いでしょうか。)

> ・基本 CRUD 機能を実装する
ここまでは何度か実装しています。


現状、書いてある通りのことしかできず応用が出来ないので、
もっと理解を深めないと、と思った次第です。
引用返信 編集キー/
■95931 / inTopicNo.12)  Re[9]: コードファーストで多対多のテーブルをカスタマイズしたい
□投稿者/ WebSurfer (2113回)-(2020/10/09(Fri) 17:22:39)
No95929 (じぇい さん) に返信

> 中間テーブルを自分で定義して使う、というところで分からなくなりました。。

それは最初の質問にあった「BookGenreモデル(中間テーブル)」エンティティクラスのこ
とですよね。であれば、

> 表示時にはジャンルの表示順を指定したく、BookGenreモデルにはSortNoを定義しています。

・・・ということだったので、SortNo フィールドを含めるには「中間テーブルを自分で定義
して使う」とせざるを得ないです。何をいまさら言っているのいう感じでなんですけど?

> (イレギュラーというよりチュートリアルと違うところと言い換えた方が良いでしょうか。)

チュートリアルでも Enrollment テーブル(中間テーブル)のエンティティクラスをプログラ
マが定義するように書かれていますよ。形としては質問者さんの最初の質問にあるものとほぼ
同じ。何が違うと思っているのですか?

Student と Course の間に多対多でリレーションを貼るだけならプログラマが中間テーブルの
エンティティクラスを定義する必要なないですが、Grade というフィールドを設けて Student
受けている Course の成績を付けたいためそうしています。

質問者さんが中間テーブルのエンティティクラスに SortNo を設けたのも同じよのような理由
と思ってましたが、違うんですか?

(ちなみに、中間テーブルのエンティティクラスをプログラマが定義しない多対多の例はチュ
ートリアルの「より複雑なデータ モデルを作成する」に説明があります)


>> ・基本 CRUD 機能を実装する
> ここまでは何度か実装しています。

であれば、やりたいこと&このスレッドの質問の目的である、、

> 「ビュー側でForeachで1対多の”多”の情報を取得する」というのが今回の目的です。

は達成できたはずですが?

いつの間にか↓これが質問になってしまったということですか?

> ナビゲーションプロパティ設定無し(できれば設定したくないので)で可能なのでしょうか?

このスレッドの元々の質問は、最初の質問にある FK が張られる先が期待通りにならないこ
とに関してだったはずですが・・・

> 現状、書いてある通りのことしかできず応用が出来ないので、
> もっと理解を深めないと、と思った次第です。

この先は自分で独力で勉強して理解を含めたいということと理解します。そうであればこの
スレッドはクローズしてください。

引用返信 編集キー/
■95943 / inTopicNo.13)  Re[9]: コードファーストで多対多のテーブルをカスタマイズしたい
□投稿者/ WebSurfer (2116回)-(2020/10/11(Sun) 15:38:15)
No95929 (じぇい さん) に返信

> 現状、書いてある通りのことしかできず応用が出来ないので、
> もっと理解を深めないと、と思った次第です。

この先は自分で独力で勉強して理解を含めたいということと理解します。そうであればこの
スレッドはクローズしてください。

そうではなくて、例えば、

> > ナビゲーションプロパティ設定無し(できれば設定したくないので)で可能なのでしょうか?

の方法とか質問があれば、なぜ設定したくないかの理由と共に、それを書いてください。
引用返信 編集キー/
■95949 / inTopicNo.14)  Re[10]: コードファーストで多対多のテーブルをカスタマイズしたい
□投稿者/ じぇい (7回)-(2020/10/12(Mon) 12:03:47)
No95943 (WebSurfer さん) に返信

とりあえず、この掲示板で質問できるレベルまで、一旦勉強と経験を積みます。

ありがとうございました。
解決済み
引用返信 編集キー/

このトピックをツリーで一括表示


トピック内ページ移動 / << 0 >>

このトピックに書きこむ