5

次の構成でEF Code Firstを使用しています

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    base.OnModelCreating(modelBuilder);
    modelBuilder.Entity<UserProfile>()
        .HasMany(x => x.TeamLeaders)
        .WithMany()
        .Map(m => m.MapLeftKey("UserId")
        .MapRightKey("TeamLeaderId")
        .ToTable("UserTeamLeaders"));
}

TeamLeaders は User の ICollection です。つまり、自己参照する多対多の関係です。

簡単に言えば、ユーザーは複数のチーム リーダーを持つことができます。この構成は正しいように見えます。予想どおり、決闘 FK/PK リンク テーブルが作成されるからです。

コレクションからチーム リーダーを編集、追加、および削除できる MVC4 アプリケーションがあります。

私のコントローラーには、もともと次のものがありました。

    var original = context.UserProfiles
        .Include("TeamLeaders")
        .Single(x => x.UserId == model.UserId);

    context.Entry(original).CurrentValues.SetValues(model);

ただし、その最後の行では TeamLeaders コレクションを更新済みとしてマークできず、SaveChanges() を呼び出したときに変更が記録されませんでした。

代わりに、リフレクションを使用して手動でプロパティをコピーする User クラスに単純な CopyProperties メソッドを作成したため、コントローラーには次のように記述されています。

    var original = context.UserProfiles
        .Include("TeamLeaders")
        .Single(x => x.UserId == model.UserId);

    //context.Entry(original).CurrentValues.SetValues(model);

    original.CopyProperties(model);

ただし、これは行き過ぎで、SaveChanges は、選択したチーム リーダーのプロファイルに一致する新しいユーザーをシステムに追加しようとします。

これのどの部分が間違っているかについて誰かアドバイスできますか? マッピングを更新する必要があるのか​​、ビュー モデルからモデルにプロパティをコピーする方法を変更する必要があるのか​​ わかりません

4

1 に答える 1

3

変更検出がどのリーダーが削除され、どのリーダーが追加されたかを認識できるように、変更に従ってユーザーにロードされTeamLeadersたコレクションを変更する必要があります。original呼び出すとSaveChanges、EF は、検出された変更に基づいて、結合テーブルに適切な DELETE および INSERT ステートメントを書き込みます。最も簡単な方法は次のようになります。

var original = context.UserProfiles
    .Include("TeamLeaders")
    .Single(x => x.UserId == model.UserId);

original.TeamLeaders.Clear();
foreach (var teamLeader in model.TeamLeaders)
{
    var user = context.UserProfiles.Find(teamLeader.UserId);
    if (user != null)
        original.TeamLeaders.Add(user)
}
context.SaveChanges();

Findチーム リーダーが既に読み込まれている場合は、コンテキストからチーム リーダーを取得します。それらがロードされていない場合、データベースにクエリを実行します。

追加のクエリを避けたい場合は、チーム リーダーをコンテキストに手動でアタッチできます。

var original = context.UserProfiles
    .Include("TeamLeaders")
    .Single(x => x.UserId == model.UserId);

original.TeamLeaders.Clear();
foreach (var teamLeader in model.TeamLeaders)
{
    var user = context.UserProfiles.Local
        .SingleOrDefault(o => o.UserId == teamLeader.UserId);
    if (user == null)
    {
        user = new User { UserId = teamLeader.UserId };
        context.UserProfiles.Attach(user);
    }
    original.TeamLeaders.Add(user)
}
context.SaveChanges();

ここにロードする最初のクエリを除いて、originalそれ以上のデータベース クエリは関係ありません。

Includeところで: EF Code Firstで強く型付けされたバージョンを使用できるはずです:

Include(u => u.TeamLeaders)

using System.Data.Entity;このバージョンにアクセスするには、コード ファイルが必要です。

于 2012-09-16T11:17:40.050 に答える