[解決済み】アソシエーションテーブルにフィールドを追加して、多対多で先にコードを作成する
質問
こんなシナリオがあります。
public class Member
{
public int MemberID { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public virtual ICollection<Comment> Comments { get; set; }
}
public class Comment
{
public int CommentID { get; set; }
public string Message { get; set; }
public virtual ICollection<Member> Members { get; set; }
}
public class MemberComment
{
public int MemberID { get; set; }
public int CommentID { get; set; }
public int Something { get; set; }
public string SomethingElse { get; set; }
}
との関連付けはどのように設定すればよいのでしょうか? フルートAPI ? または、アソシエーション・テーブルを作成する良い方法はありますか?
どのように解決するのですか?
カスタマイズされた結合テーブルで多対多のリレーションシップを作成することはできません。多対多のリレーションシップでは、EFは結合テーブルを内部で非表示に管理します。これは、モデル内にエンティティ・クラスがないテーブルです。このような追加プロパティを持つ結合テーブルを使用するには、実際に2つの一対多のリレーションを作成する必要があります。それは次のようになります。
public class Member
{
public int MemberID { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public virtual ICollection<MemberComment> MemberComments { get; set; }
}
public class Comment
{
public int CommentID { get; set; }
public string Message { get; set; }
public virtual ICollection<MemberComment> MemberComments { get; set; }
}
public class MemberComment
{
[Key, Column(Order = 0)]
public int MemberID { get; set; }
[Key, Column(Order = 1)]
public int CommentID { get; set; }
public virtual Member Member { get; set; }
public virtual Comment Comment { get; set; }
public int Something { get; set; }
public string SomethingElse { get; set; }
}
を持つメンバーのコメントをすべて検索したい場合は、次のようにします。
LastName
= 例えば、次のようなクエリです。
var commentsOfMembers = context.Members
.Where(m => m.LastName == "Smith")
.SelectMany(m => m.MemberComments.Select(mc => mc.Comment))
.ToList();
または...
var commentsOfMembers = context.MemberComments
.Where(mc => mc.Member.LastName == "Smith")
.Select(mc => mc.Comment)
.ToList();
また、名前 "Smith"(複数いると仮定)を持つメンバーのリストとそのコメントを作成するには、プロジェクションを使用できます。
var membersWithComments = context.Members
.Where(m => m.LastName == "Smith")
.Select(m => new
{
Member = m,
Comments = m.MemberComments.Select(mc => mc.Comment)
})
.ToList();
を持つメンバーのすべてのコメントを見つけたい場合、そのコメントは
MemberId
= 1:
var commentsOfMember = context.MemberComments
.Where(mc => mc.MemberId == 1)
.Select(mc => mc.Comment)
.ToList();
これで、結合テーブルのプロパティでフィルタリングすることもできます(多対多の関係では不可能です)。プロパティに 99 を持つメンバー 1 のすべてのコメントをフィルタリングします。
Something
:
var filteredCommentsOfMember = context.MemberComments
.Where(mc => mc.MemberId == 1 && mc.Something == 99)
.Select(mc => mc.Comment)
.ToList();
レイジーローディングのおかげで、物事が簡単になるかもしれません。もし、ロードされた
Member
を使えば、明示的なクエリなしでコメントを取得できるはずです。
var commentsOfMember = member.MemberComments.Select(mc => mc.Comment);
遅延ロードは、裏で自動的にコメントを取得するのでしょうね。
編集
このモデルでエンティティやリレーションシップを追加する方法と、それらを削除する方法について、いくつかの例を挙げて説明します。
1) メンバーを 1 つ作成し、このメンバーのコメントを 2 つ作成します。
var member1 = new Member { FirstName = "Pete" };
var comment1 = new Comment { Message = "Good morning!" };
var comment2 = new Comment { Message = "Good evening!" };
var memberComment1 = new MemberComment { Member = member1, Comment = comment1,
Something = 101 };
var memberComment2 = new MemberComment { Member = member1, Comment = comment2,
Something = 102 };
context.MemberComments.Add(memberComment1); // will also add member1 and comment1
context.MemberComments.Add(memberComment2); // will also add comment2
context.SaveChanges();
2) member1 の3番目のコメントを追加します。
var member1 = context.Members.Where(m => m.FirstName == "Pete")
.SingleOrDefault();
if (member1 != null)
{
var comment3 = new Comment { Message = "Good night!" };
var memberComment3 = new MemberComment { Member = member1,
Comment = comment3,
Something = 103 };
context.MemberComments.Add(memberComment3); // will also add comment3
context.SaveChanges();
}
3) 新しいメンバーを作成し、既存のcomment2と関連付ける。
var comment2 = context.Comments.Where(c => c.Message == "Good evening!")
.SingleOrDefault();
if (comment2 != null)
{
var member2 = new Member { FirstName = "Paul" };
var memberComment4 = new MemberComment { Member = member2,
Comment = comment2,
Something = 201 };
context.MemberComments.Add(memberComment4);
context.SaveChanges();
}
4) 既存の member2 と comment3 の間にリレーションを作成する。
var member2 = context.Members.Where(m => m.FirstName == "Paul")
.SingleOrDefault();
var comment3 = context.Comments.Where(c => c.Message == "Good night!")
.SingleOrDefault();
if (member2 != null && comment3 != null)
{
var memberComment5 = new MemberComment { Member = member2,
Comment = comment3,
Something = 202 };
context.MemberComments.Add(memberComment5);
context.SaveChanges();
}
5) このリレーションシップを再度削除します。
var memberComment5 = context.MemberComments
.Where(mc => mc.Member.FirstName == "Paul"
&& mc.Comment.Message == "Good night!")
.SingleOrDefault();
if (memberComment5 != null)
{
context.MemberComments.Remove(memberComment5);
context.SaveChanges();
}
6) member1 とそのコメントとの関係をすべて削除します。
var member1 = context.Members.Where(m => m.FirstName == "Pete")
.SingleOrDefault();
if (member1 != null)
{
context.Members.Remove(member1);
context.SaveChanges();
}
のリレーションを削除します。
MemberComments
の間の1対多の関係も
Member
と
MemberComments
との間の
Comment
と
MemberComments
は、慣習的にcascading deleteで設定されています。そして、このようなケースは
MemberId
と
CommentId
で
MemberComment
の外部キー・プロパティとして検出されます。
Member
と
Comment
ナビゲーションプロパティであり、FK プロパティは non-nullable 型であるため
int
が必要であり、最終的にcascading-delete-setupが発生します。このモデルには意味があるのだと思います。
関連
-
[解決済み] [Solved] 1つ以上のエンティティで検証に失敗しました。詳細は'EntityValidationErrors'プロパティを参照してください [重複]。
-
[解決済み] このコマンドに関連する開いているDataReaderがすでにあり、最初にそれを閉じる必要があります。
-
[解決済み】エラー。「戻り値を変更できません」 C#
-
[解決済み】C#におけるtypedefの等価性
-
[解決済み] 保護レベルによりアクセス不能になりました。
-
[解決済み】ソケットのアドレス(プロトコル/ネットワークアドレス/ポート)は、通常1つしか使用できない?
-
[解決済み】"指定されたパスのフォーマットはサポートされていません。"
-
[解決済み】Microsoft.Extensions.LoggingからILoggerを解決することができない
-
[解決済み】名前 'ViewBag' が現在のコンテキストに存在しない - Visual Studio 2015
-
[解決済み】Entity Framework 4.1 Code Firstでクラスプロパティを無視する
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
[解決済み】「入力文字列が正しい形式ではありませんでした」エラーの解決方法は?[重複しています]。
-
[解決済み】WPFでXamlファイルにコメントを追加する方法は?
-
[解決済み】「...は'型'であり、与えられたコンテキストでは有効ではありません」を解決するにはどうすればよいですか?(C#)
-
[解決済み】2つ(またはそれ以上)のリストを1つに統合する(C# .NETで
-
[解決済み】2年前のMSDateを把握する【クローズド
-
[解決済み】エラー「必要なフォーマルパラメータに対応する引数が与えられていない」を解決する?
-
[解決済み】ファイルやアセンブリ、またはその依存関係の1つをロードできませんでした。
-
[解決済み】プロセスが実行されているかどうかを知るには?
-
[解決済み】名前 'ViewBag' が現在のコンテキストに存在しない - Visual Studio 2015
-
[解決済み】Nullableオブジェクトは値を持たなければならない?