列表属性未由EF更新

时间:2012-12-14 17:40:30

标签: asp.net-mvc entity-framework many-to-many

好吧,这可能是一个我错了的概念,但无论如何...... (MVC3)我有一个带有list属性的实体。我的CRUD视图通过Ajax帖子向控制器发送JSon表示。一切都很好,除了当我发布该实体的更新时,list属性根本没有更新。实体的所有简单属性都会更新,但(正如我想象的)更新树不包括List属性。如何让EF了解列表中的这些更改?

到目前为止,这是一些代码:

    [HttpPost]
    public JsonResult Edit(Lote lote)
    {
            //Given the IDs present in lote.Documentos, load the List of Documentos
            if (lote.Documentos != null)
            {
                List<Documento> ldoc = new List<Documento>();
                foreach (var d in lote.Documentos)
                {
                    ldoc.Add(db.Documentos.Find(d.IDDocumento));
                }
                lote.Documentos.Clear();
                foreach (var d in ldoc)
                {
                    lote.Documentos.Add(d);
                }
            }

            //Now, clear all the previous errors
            foreach (var modelValue in ModelState.Values)
            {
                modelValue.Errors.Clear();
            }
            //And re-validate the model
            ValidateModel(lote);

            if (ModelState.IsValid)
            {
                if (lote.IDLote > 0)
                {
                    //Updating
                    db.Entry(lote).State = EntityState.Modified;
                }
                else
                {
                    //Inserting
                    db.Lotes.Add(lote);
                }
                db.SaveChanges();
                CustomMessages.Sucesso(TempData, "Informações salvas com sucesso.", 10000);
                return Json(new { Success = 1, IDProprietario = lote.IDLote, ex = "" });
            }
            else
            {
                return Json(new { Success = 0, ex = "Falha na rotina de armazenamento das informações"});
            }

这些都是班级本身:

public class Lote
{
    [Key]
    public int IDLote { get; set; }

    (... lots of properties ...)

    [Display(Name = "Documentos")]
    public List<Documento> Documentos { get; set; }
}


public class Documento
{
    //---=== ATRIBUTOS ===---
    [Key]
    public int IDDocumento { get; set; }

    [Required]
    [MaxLength(60)]
    public string Nome { get; set; }

    public List<Lote> Lotes { get; set; }
}

由于这是一个多对多的关系,我也得到了这个:

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Conventions.Remove
        <System.Data.Entity.ModelConfiguration.Conventions.PluralizingTableNameConvention>();

        modelBuilder.Entity<Lote>()
            .HasMany(t => t.Documentos)
            .WithMany(t => t.Lotes)
            .Map(m =>
            {
                m.ToTable("LoteDocumento");
                m.MapLeftKey("IDLote");
                m.MapRightKey("IDDocumento");
            });
(... and some other stuff)

对此有何帮助?

1 个答案:

答案 0 :(得分:0)

尝试更改此行:

ldoc.Add(db.Documentos.Find(d.IDDocumento));

ldoc.Add(db.Documentos.Include("Lotes").FirstOrDefault(x => x.IDDocumento == d.IDDocumento));

您需要确保您要更改的关系/对象实际上已附加到当前的DB上下文中。否则,实体框架将无法跟踪对它们所做的更改。

This link explains it in terms of object context, but I think the same rules apply to DBcontext.

如果这不起作用,请告诉我因为我真的想要更好地理解EF的工作方式。

好的,找到了一些有用的链接:

An object with the same key already exists in the ObjectStateManager. The ObjectStateManager cannot track multiple objects with the same key

Entity Framework and Connection Pooling

执行Include("Lotes")时,会将与Documentos相关的抽奖添加到DBContext。实体框架现在正在跟踪这些对象。在您的代码中,您将使用此行db.Entry(lote).State = EntityState.Modified;将其重新添加到上下文中。无论如何,这是我的猜测。

根据上面的链接,我会尝试重新编写你喜欢的内容(未编译):

[HttpPost]
public JsonResult Edit(Lote lote)
{
    //get old Lote from DB
    var oldLote = db.Lotes.include("Documentos").FirstOrDefault(x => x.IDLote == lote.IDLote);

    //update
    if(oldLote != null)
    {
        //refresh any other properties that you may have changed on it.
        db.Entry(oldLote).CurrentValues.SetValues(lote);

        //not sure if you will even need this section any more but then you can...
        oldLote.Documentos.Clear();
        foreach (var d in lote.Documentos)
        {
            oldLote.Documentos.Add(db.Documentos.include("Lotes").FirstOrDefault(x => x.IDDocumento == d.IDDocumento));
        }
    }    
    else //add
    {
        //not sure if this will work
        foreach (var d in lote.Documentos)
        {
            db.Entry(d).State = EntityState.Modified;
        }

        db.Lotes.Add(lote);
    }

    //then just save changes.  EF is already tracking all your objects and changes.
    db.SaveChanges();
....