【问题标题】:Entity Framework many-to-many relationship values not saved in database实体框架多对多关系值未保存在数据库中
【发布时间】:2014-01-04 06:49:31
【问题描述】:

我使用 Code First 和 Entity Framework 5 建立了多对多关系。连接表已创建,但字段数据未保存到数据库中。

工人已成功添加到 Project 对象中,并且 db.SaveChanges() 方法执行没有错误,但是当再次调用 /Project/Edit/x 操作的 GET 请求时,工人列表属性为空。

如何进行这项工作,以便连接的 Worker 数据在通过 POST“保存”后与 GET 请求中的 Project 对象一起保存和检索?

控制器:ProjectController.vb

POST:/Project/Create/x

    For i As Integer = LBound(strWorkerIds) To UBound(strWorkerIds)
        Dim workerId As Integer
        If Integer.TryParse(Trim(strWorkerIds(i)), workerId) Then
            Dim worker As Worker = db.Workers.Find(workerId)
            If Not worker Is Nothing Then
                project.Workers.Add(worker)
            End If
        End If
    Next

    If ModelState.IsValid Then
        db.Projects.Add(project)
        db.SaveChanges() ' The project is saved along with the correct project.Workers list
        Return RedirectToAction("Index", "Home")
    End If

POST:/Project/Edit/x

    For i As Integer = LBound(strWorkerIds) To UBound(strWorkerIds)
        Dim workerId As Integer
        If Integer.TryParse(Trim(strWorkerIds(i)), workerId) Then
            Dim worker As Worker = db.Workers.Find(workerId)
            If Not worker Is Nothing Then
                project.Workers.Add(worker)
            End If
        End If
    Next

    If ModelState.IsValid Then
        db.Entry(project).State = EntityState.Modified
        db.SaveChanges() 'The changes to project.Workers list are NOT being saved?
        Return RedirectToAction("Index", "Home")
    End If

模型:Project.vb

Public Class Project

    Public Sub New()
        Me.Workers = New List(Of Worker)()
    End Sub

    Public Property ProjectID As Integer
    'other properties...
    Public Overridable Property Workers As ICollection(Of Worker)

End Class

模型:Worker.vb

Public Class Worker

    Public Sub New()
        Me.Projects = New List(Of Project)()
    End Sub

    Public Property WorkerID As Integer
    'other properties...
    Public Overridable Property Projects As ICollection(Of Project)

End Class

数据库上下文:ProjectLogContext.vb

Imports System.Data.Entity
Imports System.Data.Entity.ModelConfiguration.Conventions

Public Class ProjectLogContext

    Inherits DbContext

    Public Property Projects As DbSet(Of Project)
    Public Property Workers As DbSet(Of Worker)

    Protected Overrides Sub OnModelCreating(modelBuilder As DbModelBuilder)
        MyBase.OnModelCreating(modelBuilder)

        modelBuilder.Conventions.Remove(Of PluralizingTableNameConvention)()

        modelBuilder.Entity(Of Worker)().HasMany(Function(w) w.Projects) _
            .WithMany(Function(p) p.Workers) _
            .Map(Function(x) x.MapLeftKey("WorkerID").MapRightKey("ProjectID") _
                 .ToTable("WorkerProject"))

    End Sub

End Class

【问题讨论】:

  • 嗨,欢迎来到 StackOverflow。只是一个建议-您可以在一组较小的代码中复制它吗,例如没有与您的问题不一定相关的 MVC 控制器代码?这里有很多代码,如果我是正确的,这是一个 EF 问题而不是 MVC 问题?
  • 感谢@iandotkelly,我已将代码删减以显示重要部分。似乎 EF 不知道 project.Workers 列表已被修改和/或它没有在数据库中更新它。如何让它更新数据库中的 project.Workers 列表?

标签: vb.net asp.net-mvc-4 entity-framework-5


【解决方案1】:

将父实体的状态设置为Modified 不会对多对多关联中的关系产生任何影响。为了实现正确的更新,您必须从数据库中加载当前实体图(父实体图,包括其子实体),然后添加一个子实体(如果其 ID 在您的 ID 列表中)或 删除 如果其 ID 在 ID 列表中不是,则为子级。 EF 的更改跟踪将跟踪已加载图形的这些修改,并生成必要的 INSERT 和 DELETE 语句来更新链接表。在您的 Edit POST 操作(在 C# 中)中,它可能看起来像这样:

var projectInDb = db.Projects.Include(p => p.Workers)
    .SingleOrDefault(p => p.ProjectID == project.ProjectID);

if (projectInDb != null)
{
    // Convert string list to integer list
    var workerIds = new List<int>();
    foreach (string strWorkerId in strWorkerIds)
    {
        int workerId;
        if (int.TryParse(strWorkerId.Trim(), out workerId))
            workerIds.Add(workerId);
    }

    // Update scalar properties of the project
    db.Entry(projectInDb).CurrentValues.SetValues(project);

    // Remove workers
    foreach (var workerInDb in projectInDb.Workers.ToList())
    {
        if (!workerIds.Contains(workerInDb.WorkerID))
            projectInDb.Workers.Remove(workerInDb);
    }

    // Add workers
    foreach (int workerId in workerIds)
    {
        if (!projectInDb.Workers.Any(w => w.WorkerID == workerId))
        {
            var workerInDb = db.Workers.Find(workerId);
            if (workerInDb != null)
                projectInDb.Workers.Add(workerInDb);
        }
    }

    db.SaveChanges();
}

【讨论】:

  • 感谢 @Slauma,正如您和 @jd3155490 正确指出的那样,我没有“包括”子工人列表,我只是假设 EF 会自动跟踪它。另外我注意到你没有在你的代码中使用修改后的状态,如果没有这个_db.Entry(project).State = EntityState.Modified;,它会工作吗?
  • @Rob144:是的。 db.Entry(projectInDb).CurrentValues... 负责更新项目属性。 (我不得不使用projectInDb 而不是project,但是,我已经解决了这个问题。)jd31... 的代码将无法正常工作,因为仅在 GET 请求中使用Include 是不够的。跨度>
【解决方案2】:

我希望您在 GET: /Project/Edit/x 操作中获取项目时“包括”附加到项目的工作人员。我在 C# 中复制了您的问题,如果我在 GET: /Project/Edit/x 操作中的 lambda 表达式中包含工作人员,我可以获得附加到项目的工作人员列表。这是 ProjectController 的代码。

readonly ProjectLogContext _db = new ProjectLogContext();
// GET: /Project/
    public ActionResult Index()
    {
        var model = _db.Projects.ToList();
        return View(model);
    }

    //GET: /Project/Create/x
    public ActionResult Create()
    {
        return View();
    }

    //POST: /Project/Create/x
    [HttpPost]
    public ActionResult Create(Project project)
    {
        string[] strWorkerIds = { "26", "27", "28" };

        for (var i = strWorkerIds.GetLowerBound(0); i <= strWorkerIds.GetUpperBound(0); i++)
        {
            int workerId;
            if (!int.TryParse(strWorkerIds[i], out workerId)) continue;
            var worker = _db.Workers.Find(workerId);
            if (worker != null)
            {
                project.Workers.Add(worker);
            }
        }

        if (!ModelState.IsValid) return View();
        _db.Projects.Add(project);
        _db.SaveChanges();
        return RedirectToAction("Index", "Project");
    }


    //GET: /Project/Create/x
    public ActionResult Edit(int id)
    {
        var query = _db.Projects.Include(p => p.Workers).Single(p => p.ProjectId == id);
        return View(query);
    }

    //POST: /Project/Create/x
    [HttpPost]
    public ActionResult Edit(Project project)
    {
        if (!ModelState.IsValid) return View();
        _db.Entry(project).State = EntityState.Modified;
        _db.SaveChanges();
        return RedirectToAction("Index", "Project");
    }

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-12-09
    • 2020-12-01
    • 2017-05-17
    • 2011-07-20
    相关资源
    最近更新 更多