Many To Many Entity Framework Update

Asked

Viewed 276 times

1

I’m having trouble at the time of updating with relationship Many to Many.

I want the update method to update all itemEntity fields as well as vendors.

For example I only put the description field and suppliers according to the screen image.

In my code below if I remove the line that gives the error (shown below) it writes only suppliers.

I tried to set itemEntity as modified but I get error.

Can someone please help me?

My Canvas

Minha Tela

Error

Attaching an Entity of type 'Domain.Entities.Itementity' failed because Another Entity of the same type already has the same Primary key value. This can happen when using the 'Attach' method or Setting the state of an Entity to 'Unchanged' or 'Modified' if any entities in the Graph have Conflicting key values. This may be because some entities are new and have not yet Received database-generated key values. In this case use the 'Add' method or the 'Added' Entity state to track the Graph and then set the state of non-new entities to 'Unchanged' or 'Modified' as appropriate.

inserir a descrição da imagem aqui

Table

inserir a descrição da imagem aqui

Relationship

inserir a descrição da imagem aqui

Code

    public override ItemEntity Atualizar(ItemEntity entity)
    {
        var novos = new List<FornecedorEntity>(entity.Fornecedores);

        entity.Fornecedores = _dbContext.TbItem
           .Where(p => p.Id == entity.Id).FirstOrDefault().Fornecedores;

        _dbContext.Entry(entity).State = EntityState.Modified;
        //Sem a linha acima ele funciona mais não atualiza as demais propriedades do ItemEntity, 
        //atualiza somente os fornecedores.

        var deletetadoFornecedores = entity.Fornecedores
            .Except(novos).ToList();

        deletetadoFornecedores.ForEach(c => entity.Fornecedores.Remove(c));

        var AdicionadoFornecedores = novos
            .Except(entity.Fornecedores).ToList();

        foreach (FornecedorEntity c in AdicionadoFornecedores)
        {
            if (_dbContext.Entry(c).State == EntityState.Detached)
            {
                _dbContext.TbFornecedor.Attach(c);
            }

            entity.Fornecedores.Add(c);
        }

        _dbContext.SaveChanges();      

        return entity;
    }

Edited

This code does exactly what I need, but I have to keep mapping the properties.

    public override ItemEntity Atualizar(ItemEntity entity)
    {
        var itemExistente = _dbContext.TbItem
                .Find(entity.Id);

        itemExistente.Descricao = entity.Descricao;
        itemExistente.Descricao1 = entity.Descricao1;
        itemExistente.Descricao2 = entity.Descricao2;
        itemExistente.Descricao3 = entity.Descricao3;
        itemExistente.Descricao4 = entity.Descricao4;
        //Esse mapeamento acima não tem como fazer de forma automatica?
        //Toda vez que eu criar uma nova propriedade vou ter que lembrar de vir aqui e alterar
        //isso gera maior dificuldade na manutenão e aumenta a chance de esquecer e gerar um bug.


        var fornecedoresDeletados = itemExistente.Fornecedores
            .Except(entity.Fornecedores).ToList();

        var fornecedoresAdicionados = entity.Fornecedores
            .Except(itemExistente.Fornecedores).ToList();

        fornecedoresDeletados.ForEach(c => itemExistente.Fornecedores.Remove(c));

        foreach (FornecedorEntity c in fornecedoresAdicionados)
        {
            if (_dbContext.Entry(c).State == EntityState.Detached)
                _dbContext.TbFornecedor.Attach(c);

            itemExistente.Fornecedores.Add(c);
        }

        _dbContext.SaveChanges();

        return entity;
    }
  • Mauricio, in place of the error image could by the message?

  • I can tell you what message?

  • I don’t know kkkk, I just can’t see the images here :(. It’s not a message you have in the Bug image?

  • rsrs, Give it a click that it gets big. I put on it anyway.

  • No, that’s not it, the network I’m on is blocked. And it’s also important for the text, because if someone searches for the error it’s easier to get here. :)

  • I think the only problem is not using the EntityState.Modified before saving the changes.

  • 1

    The problem is much bigger. I’m already working on something.

  • Opa vlw Gypsy, when I remove Entitystate.Modified it only updates suppliers.

Show 3 more comments

1 answer

2


Are you putting the Item twice in context, and Fornecedores also:

    entity.Fornecedores = _dbContext.TbItem
       .Where(p => p.Id == entity.Id).FirstOrDefault().Fornecedores;

What I would do in your place are two Viewmodels:

public class ItemViewModel
{
    public int ItemId { get; set; }
    public String Nome { get; set; }

    public ICollection<FornecedorViewModel> Fornecedores { get; set; }
}

public class FornecedorViewModel
{
    public int FornecedorId { get; set; }
    public String Nome { get; set; }
    public bool Selecionado { get; set; }
}

Here I would do:

public override ItemEntity Atualizar(ItemViewModel entity)
{
    var item = _dbContext.TbItem
                         .Include(i => i.Fornecedores)
                         .FirstOrDefault(i => i.Id == entity.Id);

    // Excluídos
    var idsFornecedoresExcluidos = item.Fornecedores
                                       .Where(f => !f.Selecionado)
                                       .Select(f => f.Id)
                                       .ToList();

    foreach (var fornecedor in item.Fornecedores.Where(f => idsFornecedoresExcluidos.Contains(f.Id)))
    {
        item.Fornecedores.Remove(fornecedor);
    }

    __dbContext.SaveChanges();

    // Incluídos
    var idsFornecedoresIncluidos = item.Fornecedores
                                       .Where(f => f.Selecionado)
                                       .Select(f => f.Id)
                                       .ToList();

    foreach (var novoFornecedor in __dbContext.Fornecedores.Where(f => idsFornecedoresIncluidos.Contains(f.Id)))
    {
        item.Fornecedores.Add(novoFornecedor);
    }

    __dbContext.SaveChanges();

    // Alterando item.
    item.Nome = entity.Nome;
    _dbContext.Entry(item).State = EntityState.Modified;
    __dbContext.SaveChanges();

    return item;
}

As there are several operations, a transactional scope here would be interesting:

public override ItemEntity Atualizar(ItemViewModel entity)
{
    using (var scope = new TransactionScope()) // Adicione a referência para System.Transactions para ter isso funcionando
    {
        var item = _dbContext.TbItem
                             .Include(i => i.Fornecedores)
                             .FirstOrDefault(i => i.Id == entity.Id);

        // Excluídos
        var idsFornecedoresExcluidos = item.Fornecedores
                                           .Where(f => !f.Selecionado)
                                           .Select(f => f.Id)
                                           .ToList();

        foreach (var fornecedor in item.Fornecedores.Where(f => idsFornecedoresExcluidos.Contains(f.Id)))
        {
            item.Fornecedores.Remove(fornecedor);
        }

        __dbContext.SaveChanges();

        // Incluídos
        var idsFornecedoresIncluidos = item.Fornecedores
                                           .Where(f => f.Selecionado)
                                           .Select(f => f.Id)
                                           .ToList();

        foreach (var novoFornecedor in __dbContext.Fornecedores.Where(f => idsFornecedoresIncluidos.Contains(f.Id)))
        {
            item.Fornecedores.Add(novoFornecedor);
        }

        __dbContext.SaveChanges();

        // Alterando item.
        item.Nome = entity.Nome;
        _dbContext.Entry(item).State = EntityState.Modified;
        __dbContext.SaveChanges();

        scope.Complete(); // Não esqueça do `scope.Complete()` para completar a transação.
        return item;
    }
}

If you need help with how to load Viewmodel, only if expressed by comment.

  • do not have to do all operations with a single _dbContext.Savechanges(); ? I am using Unitofwork which at the end of everything I call _dbContext.Savechanges();, after I solved the problem I was going to remove _dbContext.Savechanges(); from that class.

  • 1

    Why are you using Unitofwork? Read this.

  • I’m using a DDD architecture that I’m learning and not all changes will always be within the same repository.

  • I managed to do another way according to the link , I will post the code and then you answer me about my doubt that I will highlight, just a moment. http://www.entityframeworktutorial.net/EntityFramework4.3/update-many-to-many-entity-using-dbcontext.aspx

  • take a look at the last bit of code, have a doubt.

  • DDD, right? Good, good luck with your implementation. My help ends here.

  • 2

    @Mauricioferraz I advise you to read the article that the Gypsy referred in his comment, solved many problems that I had and can help you to have a better understanding of the whole project.

  • 1

    I’m reading, Thanks Gypsy for your help and the others.

Show 3 more comments

Browser other questions tagged

You are not signed in. Login or sign up in order to post.