EF6: Error while Saving

Asked

Viewed 98 times

0

I have the following scenario that I’m in trouble:

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 the error is generated):

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:

inserir a descrição da imagem aqui

My Action:

public ActionResult Create(ProdutoViewModel produto)
{
    if (ModelState.IsValid)
    {
        var produtoDomain = MapearParaDomainModel(produto);
        produtoDomain.Categoria = _categoriaApp.ObterPorId(produto.CategoriaId);
        _produtoApp.Adicionar(produtoDomain);

        return RedirectToAction("Index");
    }

    return null;
}

Error message:

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

As I am adding the product no need to search the category to put in the object to then record, so I removed the line "product Omain.Category = ..." from the action and also removed the line "_db. Entry(product.Category)..." of the write method, the error continues but its type is not specified.

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 when creating the entity Product Voce can set the Categoriaid = idCategory without searching 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 being searched in one context and added the reference in another context, in your case, _productApp and _categoriaApp were not created with the same context. But as your mistake persisted in removing this information, I believe that is not the case.

1

You’ll have to do the EntityState.Unchanged for each Category instance 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, as you are marking your Category objects unchanged, you use the _db.Configuration.LazyLoadingEnabled = false if it keeps the categories in another repository or method, and so it does not load the categories together with the product when it comes from the database.

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();
    }
}
  • Thanks Jhon! You said you have to make the "Unchanged" for each Product Instance Category, but the product only has one category. I also disabled Lazy Loading but it didn’t work either (I guess it didn’t influence the recording only in the data search, right?).

  • If you are saving only the product you would not also need to mark the category as unchanged.

  • Lazyloading also works to keep a record if you search the database to find out if it is to update or add... it will not load the aggregated objects and will not have to user the "Unchanged" for each charged instance.

  • Another check we can do is, before saving (apply Savechanges) is to look at the objects that are mapped in the Entity for database changes. It would be in _db.Product.Local (with the debug to navigate these levels and check if there is only that entity). If you have more than one object mapped to the same application instance, I recommend using Asnotraking in queries. Sorry for the delay in returning.

Browser other questions tagged

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