Edit object with related objects

2

I have an object Cliente that relates to two other objects.

[Table("Cliente")]
public class Cliente
{
    [StringLength(100, ErrorMessage = "Este campor permite apenas 100 caracteres"), Required]
    public String Nome { get; set; } ...

    public virtual Titulo Titulo { get; set; }

    public virtual ICollection<Dependentes> Dependentes { get; set;}
}

That relates to Titulo

[Table("Titulo")]
public class Titulo
{
    [Key, ForeignKey("Cliente")]
    public Guid TituloId { get; set; }

    public DateTime DataCadastro { get; set; }

    public DateTime DataExpiracao { get; set; }

    //Relacionamento tabela Cliente
    public virtual Cliente Cliente { get; set; }
}

And with Dependentes

[Table("Dependentes")]
public class Dependentes
{
    public Guid DependentesId { get; set; }
    public Guid ClienteId { get; set; }

    public String Nome { get; set; }

    public virtual Cliente Cliente { get; set; }
}

I'm trying to edit this object, and those that relate to it, using the following method in my controller . I left some in comments as it was in some ways that I tried.

[HttpPost]
public async Task<ActionResult> Editar(Cliente cliente)
{
    using (var scope = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled))
    {
       var cli = await db.Clientes
                                .Include(c => c.Titulo)
                                .Include(d => d.Dependentes)
                                .FirstOrDefaultAsync(c => c.ClienteId == cliente.ClienteId);

        // Dependentes Originais
        var denpendentesOriginais = await db.Dependentes.AsNoTracking().Where(ct => ct.Cliente.ClienteId == cli.ClienteId).ToListAsync();
        if (cliente.Dependentes != null)
        {
            // Dependentes Excluídos
            foreach (var dependenteOriginal in denpendentesOriginais)
            {
                if (!cliente.Dependentes.Any(dp => dp.DependentesId == dependenteOriginal.DependentesId))
                {
                    var dependenteExcluido = await db.Dependentes.SingleAsync(dp => dp.DependentesId == dependenteOriginal.DependentesId);
                    db.Dependentes.Remove(dependenteExcluido);
                    await db.SaveChangesAsync();
                }
            }

            // Dependentes Inseridos ou Alterados
            foreach (var dependente in cliente.Dependentes)
            {
                if (!denpendentesOriginais.Any(dp => dp.DependentesId == dependente.DependentesId))
                {
                    // Dependente não existe ainda. Inserir.
                    dependente.ClienteId = cliente.ClienteId;
                    db.Dependentes.Add(dependente);
                }
                else
                {
                    // Dependente já existe. Marcar como alterado.
                    db.Entry(dependente).State = EntityState.Modified;
                }

                await db.SaveChangesAsync();
            }
        }
            ViewBag.PossibleUsuarios = db.Users;
            scope.Complete();
            return View(cliente);
        }
    }

In this code snippet,

else
{
   // Dependente já existe. Marcar como alterado.
   db.Entry(dependente).State = EntityState.Modified;
}

I have an Error:

  

Attaching an entity of type 'SalesTables.Models.Dependents' 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 has conflicting key values. This may be because some   entities are new and have not 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.

I'm not sure if I implemented it right, I imagine it did not, or if something was missing.

How can I do this?

Is there a better way to solve this problem than what I'm doing?

    
asked by anonymous 08.05.2017 / 14:23

1 answer

3

This code is useless and is causing your error:

    Titulo titulo = await db.Titulos.SingleAsync(t => t.TituloId == cli.ClienteId);
    db.Entry(titulo).State = EntityState.Modified;
    await db.SaveChangesAsync();

Here you select Titulo of the bank, mark it as changed and saved. The Entity Framework did not notice any changes and the object is observed throughout the remainder of the execution.

I also do not understand what this if has to do with dependency logic:

if (cliente.Titulo != null) { ... }

This logic is not good:

        if (!cliente.Dependentes.Any(ct => ct.ClienteId == dependenteOriginal.ClienteId))
        {
            var dependenteExcluido = db.Dependentes.Single(ct => ct.DependentesId == dependenteOriginal.Cliente.ClienteId);
            db.Dependentes.Remove(dependenteExcluido);
            await db.SaveChangesAsync();
        }

Here you are not necessarily selecting the dependent by his key, which can bring another dependent and delete it unduly. The right thing would be:

        if (!cliente.Dependentes.Any(dp => dp.DependenteId == dependenteOriginal.DependenteId))
        {
            var dependenteExcluido = db.Dependentes.Single(dp => dp.DependenteId == dependenteOriginal.DependenteId);
            db.Dependentes.Remove(dependenteExcluido);
            await db.SaveChangesAsync();
        }

Same thing for the one below:

    // Dependentes Inseridos ou Alterados
    foreach (var dependente in cliente.Dependentes)
    {
        if (!denpendentesOriginais.Any(dp => dp.DependenteId == dependente.DependenteId))
        {
            // Dependente não existe ainda. Inserir.
            dependente.ClienteId = cliente.ClienteId;
            db.Dependentes.Add(dependente);
        }
        else
        {
                // Dependente já existe. Marcar como alterado.
            db.Entry(dependente).State = EntityState.Modified;
        }

        await db.SaveChangesAsync();
    }

Remove this part too, which is only disrupting:

var cli = await db.Clientes
                            .Include(c => c.Titulo)
                            .Include(d => d.Dependentes)
                            .FirstOrDefaultAsync(c => c.ClienteId == cliente.ClienteId);
    
08.05.2017 / 16:19