Entity Frameworkコア - 更新関連コレクション

asp.net-core c# entity-framework entity-framework-core

質問

ProjectModel内のProjectEmployeesのコレクションを更新しようとしています。古い値をすべて削除して新しい値を設定したい。

私のモデル:

public class Project
{
    ... 
    public ICollection<ProjectEmployee> ProjectEmployees { get; set; }
}

public class ProjectEmployee
{
    public int ProjectId { get; set; }
    public virtual Project Project { get; set; }
    public int UserId { get; set; }
    public virtual Employee Employee { get; set; }
}

public class Employee
{
    public int UserId { get; set; }
    public User User { get; set; }
    ...
}
public class ProjectGroupModel //ViewModel
{
    public int ProjectId { get; set; }
    public ICollection<Employee> ProjectEmployees { get; set; }
}

典型的な多対多の関係です。

私のコントローラのアクション:

    [HttpPost("group")]
    public async Task<IActionResult> CreateGroup([FromBody] ProjectGroupModel pro)
    {
            var dbProject = await _context.Project
                .Include(p=>p.ProjectEmployees)
                .FirstAsync(p => p.ProjectId == pro.ProjectId);
            dbProject.ProjectEmployees.Clear();

            foreach (var emp in pro.ProjectEmployees)
            {
                dbProject.ProjectEmployees.Add(new ProjectEmployee()
                {
                    UserId = emp.UserId
                });
            }

            await _context.SaveChangesAsync();

            return Ok();
    }

proProjectEmployeesが空の場合、dbProject.ProjectEmployeesのすべてのレコードが正しく削除されました.dbProject.ProjectEmployeesが空の場合もモデルからの新しいレコードが追加されましたが、dbProject.ProjectEmployeesが空でない場合は新しいレコードを設定できません。

エラー:

"エンティティタイプ 'ProjectEmployee'のインスタンスは、同じキーを持つこのタイプの別のインスタンスが既に追跡されているため追跡できません。新しいエンティティを追加するとき、ほとんどのキータイプに対して、キーが設定されていない場合、つまり、キープロパティにそのタイプのデフォルト値が割り当てられている場合)新しいエンティティのキ​​ー値を明示的に設定する場合は、既存のエンティティまたは他の新しいエンティティに対して生成された一時的な値と衝突しないようにします。特定のキー値を持つエンティティインスタンスが1つだけコンテキストに関連付けられます。

私は何百という方法でこの行動を修復しようとしましたが、常にsthは間違っています。

受け入れられた回答

それは完璧ではありませんが、私がそれを動作させる唯一の方法は、対応するDbSetからアイテムを削除し、新しいものを追加する前にSaveChangesを呼び出すことです。

var dbProject = await _context.Project
    .Include(p=>p.ProjectEmployees)
    .FirstAsync(p => p.ProjectId == pro.ProjectId);

if (dbProject.ProjectEmployees.Any())
{
    _context.ProjectEmployee.RemoveRange(dbProject.ProjectEmployees);
    await _context.SaveChangesAsync();
}

foreach (var emp in pro.ProjectEmployees)
{
    dbProject.ProjectEmployees.Add(new ProjectEmployee()
    {
        UserId = emp.UserId
    });
}

await _context.SaveChangesAsync();

人気のある回答

別のSOの質問にリンク

私はあなたのクラスでここでそれに答えることができます。

次に、この拡張子を使って未選択を削除し、新しく選択したリストをリストに追加します

    public static void TryUpdateManyToMany<T, TKey>(this DbContext db, IEnumerable<T> currentItems, IEnumerable<T> newItems, Func<T, TKey> getKey) where T : class
    {
        db.Set<T>().RemoveRange(currentItems.Except(newItems, getKey));
        db.Set<T>().AddRange(newItems.Except(currentItems, getKey));
    }

    public static IEnumerable<T> Except<T, TKey>(this IEnumerable<T> items, IEnumerable<T> other, Func<T, TKey> getKeyFunc)
    {
        return items
            .GroupJoin(other, getKeyFunc, getKeyFunc, (item, tempItems) => new { item, tempItems })
            .SelectMany(t => t.tempItems.DefaultIfEmpty(), (t, temp) => new { t, temp })
            .Where(t => ReferenceEquals(null, t.temp) || t.temp.Equals(default(T)))
            .Select(t => t.t.item);
    }

それを使用してこのように見える

 var model = db.Employees
             .Include(x => x.ProjectEmployees)
             .FirstOrDefault(x => x.EmployeeId == employee.EmployeeId);

 db.TryUpdateManyToMany(model.ProjectEmployees, listOfNewProjectIds
 .Select(x => new ProjectEmployee
 {
     ProjectId = x,
     EmployeeId = employee.EmployeeId
 }), x => x.ProjectId );


Related

ライセンスを受けた: CC-BY-SA with attribution
所属していない Stack Overflow
このKBは合法ですか? はい、理由を学ぶ
ライセンスを受けた: CC-BY-SA with attribution
所属していない Stack Overflow
このKBは合法ですか? はい、理由を学ぶ