EF6: Error Recording

0

I have the following scenario that I'm having problem:

Category:

public class Categoria
{
    public int Id { get; set; }

    public string Descricao { get; set; }

    public virtual ICollection<Produto> Produtos { get; set; }
}

Product:

public class Produto
{
    public int Id { get; set; }

    public string Descricao { get; set; }

    public string Detalhes { get; set; }

    public double Preco { get; set; }

    public bool Disponivel { get; set; }

    public int CategoriaId { get; set; }

    public virtual Categoria Categoria { get; set; }

    public virtual ICollection<Cliente> Clientes { get; set; }
}

Settings with Fluent Api:

public class CategoriaConfig : EntityTypeConfiguration<Categoria>
{
    public CategoriaConfig()
    {
        ToTable("Categoria");

        HasKey(x => x.Id);

        Property(x => x.Descricao).IsRequired().HasMaxLength(100);

        HasMany(x => x.Produtos);
    }
}


public class ProdutoConfig : EntityTypeConfiguration<Produto>
{
    public ProdutoConfig()
    {
        ToTable("Produto");

        HasKey(x => x.Id);

        Property(x => x.Descricao).IsRequired().HasMaxLength(100);

        Property(x => x.Detalhes).IsRequired().HasMaxLength(100);

        Property(x => x.Preco).IsRequired();

        HasMany(x => x.Clientes);

        HasRequired(x => x.Categoria);
    }
}

Method to add the product (where you are generating the error):

public void Adicionar(Produto produto)
{
    _db.Entry(produto.Categoria).State = EntityState.Unchanged;
    _db.Set<Produto>().Add(produto);
    _db.SaveChanges();
}

The way the object is being passed to the Add method:

MyAction:

publicActionResultCreate(ProdutoViewModelproduto){if(ModelState.IsValid){varprodutoDomain=MapearParaDomainModel(produto);produtoDomain.Categoria=_categoriaApp.ObterPorId(produto.CategoriaId);_produtoApp.Adicionar(produtoDomain);returnRedirectToAction("Index");
    }

    return null;
}

Error Message:

An entity object cannot be referenced by multiple instances of IEntityChangeTracker.

As I'm adding the product, there's no need to look for the category to put on the object, and then I've removed the line "productDomain.Category = ..." from the action and also removed the line "_db.Entry (product. Category) ... "of the write method, the error continues however the type is not specified.

    
asked by anonymous 31.10.2016 / 17:42

2 answers

1

I usually use the following idea when persisting information in the bank using EF:

    public void AdicionarOuAtualizar(T entidade)
    {
        if (entidade.Id == default(int))
        {
            _contexto.Entry(entidade).State = EntityState.Added;
        }
        else
        {
            _contexto.Entry(entidade).State = EntityState.Modified;
        }
        _context.SaveChanges();
    }

And at the time of creating the Product entity you can set the CategoryId = categoryID without fetching the object if it is not necessary.

The first time I read your question I thought the reason was exactly the category object, it could be fetched in one context and added the reference in another context, in your case, _App_product and _categoriaApp were not created with the same context . But as your error persisted by removing this information, I believe that is not the case.

    
04.11.2016 / 04:08
1

You will have to make EntityState.Unchanged for each Category instance that is in the Products collection.

For example:

public void Adicionar(Produto produto)
{

    foreach(var cat in produto.Categoria)
       _db.Entry(cat).State = EntityState.Unchanged;

    _db.Set<Produto>().Add(produto);
    _db.SaveChanges();
}

Or,

public void Adicionar(Produto produto)
{
    /* Ele busca todas as categorias que estão mapeadas no contexto do banco */
    foreach(var catLocal in _db.Categoria.Local)
    {
       /*Apenas uma garantia que será alterado o status das categorias apenas do produto que está sendo trabhado.*/
       if(catlocal.ProdutoId.Equals(produto.ProdutoId)
           _db.Entry(catLocal).State = EntityState.Unchanged;
    }
    _db.Set<Produto>().Add(produto);
    _db.SaveChanges();
}

Or, since you're tagging your Category objects as unchanged, you use _db.Configuration.LazyLoadingEnabled = false if you keep the categories in another repository or method, so it does not load the categories along with the product when it comes from the database.

It would look like this:

public class ProdutoRepositorio
{
    ContextoBanco _db;

    public ProdutoRepositorio()
    {

        _db = new ContextoBanco();
        /* Instrução ao entity para que não carregue seus agregados*/
        _db.Configuration.LazyLoadingEnabled = false;
    }

    public void Adicionar(Produto produto)
    {
        /* Assim você não terá os objetos de categoria vinculados ao produto podendo fazer a lógica para manter a(s) categorias em um outro momento. */
        /*
        foreach(var cat in produto.Categoria)
           _db.Entry(cat).State = EntityState.Unchanged;*/

        _db.Set<Produto>().Add(produto);
        _db.SaveChanges();
    }
}
    
01.11.2016 / 21:15