Entity Framework 6 tries to insert FK

4

I have a small application in C #, WPF and EF6.

I'm using Code First , but I'm having a hard time creating a relationship. Look at my code, of the classes I want to relate: I am editing for the final version, if anyone has the same doubt.

public class Caixa
{
    [Key]
    public int CaixaId { get; set; }
    public DateTime DataAbertura { get; set; }
    public DateTime DataFechamento { get; set; }
    public Boolean Aberto { get; set; }
    public Decimal Troco { get; set; }
    public Decimal TotalRetiradas { get; set; }
    public Decimal TotalEntradas { get; set; }
    public Decimal Total { get; set; }
    public Decimal TotalEmCaixa { get; set; }
    public String Observacoes { get; set; }
    public Usuario Responsavel { get; set; }        

    public Caixa()
    {
        DataAbertura = DateTime.Now;
    }

}

public class Usuario
{
    [Key]    
    public int UsuarioId { get; set; }        
    [Required, Index(IsUnique=true)]
    public String Login { get; set; }
    [Required]
    public String Password { get; set; }
    public Boolean Administrador { get; set; }

}

 public class Contexts: DbContext
{
    //entidades
    public DbSet<Usuario> Usuarios { get; set; }
    public DbSet<Produto> Produtos { get; set; }
    public DbSet<Caixa> Caixas { get; set; }

    public Contexts()
    {
        Database.SetInitializer(new DropCreateDatabaseIfModelChanges<Contexts>());
    }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {    

        modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
        base.OnModelCreating(modelBuilder);            
    }

}

The code that saves is as follows:

database.Caixas.Add(NovoCaixa);
NovoCaixa.Responsavel = database.Usuarios.Single(u => u.UsuarioId == UsuarioAtivo.UsuarioId);
database.SaveChanges();

Whenever I try to save the Entity Box, I get this error:

System.Data.SqlServerCe.SqlCeException: A duplicate value cannot be inserted into a unique index. [ Table name = Usuario, Constraint name = IX_Login ]

I suspect that it is trying to insert the user, but it is not what I want, since it already exists.

Can anyone help me? I'm doing this project to learn EF.

    
asked by anonymous 23.07.2015 / 03:44

1 answer

6

Possibly you created the Model Usuario , entered two users with the same Login and attempted to execute a Migration to get the error.

Revert all Migrations using one of two commands:

PM> Update-Database -TargetMigration:0

Or

PM> Update-Database -TargetMigration:$InitialDatabase 

Then delete all Migrations and generate another Migration . Please execute another Update-Database .

EDIT

You're using wrong AddOrUpdate . This way, the configuration always tries to insert a Usuario , regardless of whether it exists or not.

The correct way is:

database.Caixas.AddOrUpdate(c => c.Login, caixa); 
database.SaveChanges();

EDIT 2

Basically, everything is wrong in your solution.

You do not need to implement a DAL (not DAO, because DAL is the name of the layer, Database Abstraction Layer , and DAO is the object itself, Database Abstraction Object ). The Entity Framework already fully implements a DAL, so you do not have to reinvent the wheel.

Possibly you want to isolate the logic in a service layer, but WPF is an MVVM pattern, in which basically it is the screen that performs the objects persistence, so it is better to give up isolating anything until it is well understood the Entity Framework works.

As I said, this is not how you persist an object at the base:

database.Caixas.AddOrUpdate(caixa);
database.Entry(caixa).Property("ResponsavelId").EntityEntry.State = EntityState.Unchanged;
database.SaveChanges();

AddOrUpdate is not a command for routine operations . It is a command for you to make initial insertions for your system to work (a practice also known as "sowing the base"). It is more common in Web projects, especially using ASP.NET MVC.

To insert a new object, the correct command is:

database.Caixas.Add(caixa);

To upgrade:

database.Entry(caixa).State = EntityState.Modified;

Do not try to make a method to perform both operations. The Entity Framework does not work that well. However, if you really want to use only one code to perform both operations (which you do not need), you can check if the object's key is null:

if (caixa.CaixaId == null) 
{
    database.Caixas.Add(caixa);
} else 
{
    database.Entry(caixa).State = EntityState.Modified;
}

database.SaveChanges();

Notice the absurdity of the code below:

database.Entry(caixa).Property("ResponsavelId").EntityEntry.State = EntityState.Unchanged;

You are taking a navigation property and trying to tell the context that it has not been modified, but you are inserting a new Caixa with a Responsavel hanging on the entity. The Entity Framework interprets this Responsavel as a new object. As much as you say that Responsavel is not a new object, the Entity Framework understands that it is because Caixa is a new object. So your mistake.

I do not know how you're doing to create this Caixa , but the two right and most common ways are:

  • Creating the object and defining a Responsavel , selecting Responsavel if this is necessary;
  • Receiving a Caixa filled by a screen and sent to the code that contains DbContext .

For your case, which is in WPF, the first form is possibly the most likely for this scenario. That is, the creation of a Caixa should look like this:

var responsavel = database.Usuarios.Single(/* Coloque aqui a condição para selecionar um Responsavel */); 
var caixa = new Caixa 
{
    Responsavel = responsavel
    /* Não defina ResponsavelId. O Entity Framework deduz ResponsavelId porque você já preencheu o Responsavel */
    /* Preencha aqui as demais properties */
};

databases.Caixas.Add(caixa);
databases.SaveChanges();
    
23.07.2015 / 04:18