4

I have an ASP.NET WebForms project with N-Layers using Entity Framework 5. I have two entities: Cliente and Banda. One Cliente may have many Banda's, and one Banda may have many Cliente's

In the bussines layer I have this code:

public void Update(Cliente cliente)
    {
        using (MegaStudioEntities contexto = new MegaStudioEntities())
        {
            if (contexto.Entry(cliente).State == EntityState.Detached)
                contexto.Entry(cliente).State = EntityState.Modified;

            //Delete existing relations
            var qBandas = from qb in contexto.Bandas.Where(b => b.Clientes.Any(c => c.IdCliente == cliente.IdCliente))
                          select qb;


            foreach (Banda b in qBandas.ToList())
                ((IObjectContextAdapter)contexto).ObjectContext.ObjectStateManager.ChangeRelationshipState(cliente, b, c => c.Bandas, EntityState.Deleted);

            contexto.SaveChanges();

            //Adding new relations
            foreach (Banda banda in cliente.Bandas)
            {
                contexto.Bandas.Attach(banda);
                ((IObjectContextAdapter)contexto).ObjectContext.ObjectStateManager.ChangeRelationshipState(cliente, banda, c => c.Bandas, EntityState.Added);
            }

            cliente.TipoCliente = contexto.TipoClientes.Find(cliente.IdTipoCliente);
            cliente.FechaModificacion = System.DateTime.Now;
            Encriptar(cliente);
            contexto.SaveChanges();
        }
    }

The first time I call Update method, run sucessfully, but the second time I get this error:

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

What I forget to close? This is the correct way to update many to many relations in EF5?

Thanks in advance!!!

Martin

UPDATE 1:

Finally my code is like that:

public void Update(Cliente cliente)
        {
            using (MegaStudioEntities contexto = new MegaStudioEntities())
            {
                Cliente savedClient = contexto.Clientes.Find(cliente.IdCliente);

                foreach (var banda in savedClient.Bandas.ToList())
                {
                    savedClient.Bandas.Remove(contexto.Bandas.Find(banda.IdBanda));
                }

                foreach (var banda in cliente.Bandas)
                {
                    savedClient.Bandas.Add(contexto.Bandas.Find(banda.IdBanda));
                }

                contexto.Entry(savedClient).CurrentValues.SetValues(cliente);
                contexto.SaveChanges();
            }
        }

Thanks Gert Arnold!!!

mneu
  • 427
  • 3
  • 14

1 Answers1

4

You don't really have to attach any object to the context. So you can prevent this exception by not doing that.

public void Update(Cliente cliente)
{
    using (MegaStudioEntities contexto = new MegaStudioEntities())
    {
        Cliente savedClient = contexto.TipoClientes.Find(cliente.IdCliente);

        foreach (var banda in savedClient.Bandas.ToList())
        {
            savedClient.Bandas.Remove(banda);
        }
        foreach (var banda in cliente.Bandas)
        {
            savedClient.Bandas.Add(banda);
        }

        savedClient.IdTipoCliente = cliente.IdTipoCliente;
        savedClient.FechaModificacion = System.DateTime.Now;

        Encriptar(cliente);
        contexto.SaveChanges();
    }
}

I'm not sure if this break code in Encriptar(cliente); because (obviously) I don't know what happens there.

As you see, you add and remove associations in a m:m relationship by adding/removing objects. You hardly ever (probably never) need to manipulate relationship state explicitly. If you feel a need to do that it most likely indicates that you overlook an easier way to achieve what you want.

Gert Arnold
  • 105,341
  • 31
  • 202
  • 291
  • Hi, thanks for the aswer! What about the rest of the porperties of cliente?, have i to do this: "savedClient.Name = cliente.Name;"? – mneu Dec 04 '12 at 13:28
  • Only if it was changed. Basically you have to copy the properties that were allowed to be changed in the UI. – Gert Arnold Dec 04 '12 at 13:39
  • The only thing I need to change from your code is in this line: "savedClient.Bandas.Remove(contexto.Bandas.Find(banda.IdBanda));", because banda is not attached to the context. Thanks for your solution!, the only thing I don't like is manually assign all the properties. Thanks! – mneu Dec 04 '12 at 14:21
  • You can also use [`context.Entry(x).CurrentValues.SetValues`](http://stackoverflow.com/q/11705569/861716). – Gert Arnold Dec 04 '12 at 14:36
  • Seems nice!, can you give me an example in this case? – mneu Dec 04 '12 at 15:09
  • Try `contexto.Entry(savedClient).CurrentValues.SetValues(cliente)` – Gert Arnold Dec 04 '12 at 15:18