1

Ok, this is probably a concept that i've got wrong, but anyways... (MVC3) I have an entity with a list property on it. My CRUD views work sending a JSon representation to the controller, via an Ajax post. Everything is working great, except that when i'm posting an update of that entity, the list property is not being updated at all. All the simple properties of the entity are updated, but (as I imagine) the update tree is not including the List property. How can I make the EF aware of those changes on the list?

Here's some of the code so far:

    [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"});
            }

And those are the classes themselves:

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; }
}

As this is a Many-to-Many relationship, i also got this:

    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)

Any help on this?

Marcelo Myara
  • 2,368
  • 23
  • 34

1 Answers1

0

Try changing this line:

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

to

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

You need to make sure that the relatioship/objects that you are changing are in fact attached to your current DB context. Otherwise entity framework wont be able to track changes made to them.

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

If that doesn't work, let me know cause I am really trying to get better at understanding the way EF works.

Okay, found some more link that will help:

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

When you execute Include("Lotes") that adds your lotes related to your Documentos to the DBContext. Entity framework is now tracking these objects. Further down in your code you are re-adding them to the context with this line db.Entry(lote).State = EntityState.Modified; That's my guess anyway.

based on the links above i would try and re-write what you have like this (not compiled):

[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();
....
Community
  • 1
  • 1
Ben Tidman
  • 2,119
  • 15
  • 29
  • nope... Now, when it reaches the: db.Entry(lote).State = EntityState.Modified; it throws an exception (saying that there's already an object with the same key on the ObjectStateManager. When I added the Include, it told the EF to bring the Documento linked to an Lote. By doing this, I got 2 of the same Lote being "watched over" by EF, right? And that's the reason for the exception... :/ – Marcelo Myara Dec 15 '12 at 00:22
  • I may be completely wrong, but... I do think that the EF isn't watching the Lote until i reach the "db.Entry(lote).State = EntityState.Modified" or the "db.Lotes.Add(lote)". I'm messing with the Documentos property list before that, so EF isn't taking care of that yet. And this property is "late bind" so when I finally reach one of those 2 lines, EF isn't aware that i changed some of the obsject in that list. What do you think? – Marcelo Myara Dec 15 '12 at 00:25
  • I just found something that probably answer this, and has lots of enlightments too. See [this](http://www.asp.net/mvc/tutorials/getting-started-with-ef-using-mvc/updating-related-data-with-the-entity-framework-in-an-asp-net-mvc-application). The key is loading a new instance of the Lote with eager load of Documentos. Then using "TryUpdateModel" to update the properties based on the bounded data posted from the view to the controller. I'll try this and then post the results... – Marcelo Myara Dec 15 '12 at 13:09
  • It is kind of the same logic on your second guess... Cause you correctly sugested me to use a new instance eagerly binded and the update the properties (but in your sugestion I was suposed to do it sort of manually and the TryUpdateModel will do it - or at least try to do it - automatically). – Marcelo Myara Dec 15 '12 at 13:13