Questions about relationship and mapping with Fluent API for EF 6

Asked

Viewed 2,078 times

2

Whenever I have a class with properties that are of the type of other classes, which at the database level represents a foreign key, I will always need the navigation properties?

And look at this example:

Revenda -> ClienteRevenda -> Empresa -> ClienteEmpresa

public class Revenda
{
    [Key]
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int Id {get; set;}
    public string Nome {get; set;}
    // Navigation Property
    public ICollection<ClienteRevenda> Clientes {get; set;}
}

public class ClienteRevenda
{
    [Key, Column(0)]
    public int RevendaId {get; set;}
    [ForeignKey("RevendaId")]
    public Empresa Empresa {get; set;}

    [Key, Column(1)]
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int Id {get; set;}
    public string Nome {get; set;}
    // Navigation Property
    public ICollection<Empresa> Empresas {get; set;}
}

public class Empresa
{
    [Key, Column(0)]
    public int RevendaId {get; set;}
    [ForeignKey("RevendaId")]
    public Revenda Revenda {get; set;}

    [Key, Column(1)]
    public int ClienteRevendaId {get; set;}
    [ForeignKey("RevendaId, ClienteRevendaId")]
    public ClienteRevenda ClienteRevenda {get; set;}

    [Key, Column(2)]
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int Id {get; set;}
    public string Nome {get; set;}
    // Navigation Property
    public ICollection<ClienteEmpresa> ClientesEmpresa {get; set;}
}

public class ClienteEmpresa
{
    [Key, Column(0)]
    public int RevendaId {get; set;}
    [ForeignKey("RevendaId")]
    public Revenda Revenda {get; set;}

    [Key, Column(1)]
    public int ClienteRevendaId {get; set;}
    [ForeignKey("RevendaId, ClienteRevendaId")]
    public ClienteRevenda ClienteRevenda {get; set;}

    [Key, Column(2)]
    public int EmpresaId {get; set;}
    [ForeignKey("RevendaId, ClienteRevendaId, EmpresaId")]
    public Empresa Empresa {get; set;}

    [Key, Column(3)]
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int Id {get; set;}
    public string Nome {get; set;}
}

Note that I am making composite keys because I need unique records. In that case, I would need to add in the Resell Properties class to Empresa and ClienteEmpresa also?

What would this mapping look like with the Fluent API?

public class RevendaMap : EntityTypeConfiguration<Revenda>
{
    public RevendaMap()
    {
        HasKey(p => p.Id);
        Property(p => p.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
    }
}

public class ClienteRevendaMap : EntityTypeConfiguration<ClienteRevenda>
{
    public ClienteRevendaMap()
    {
        HasKey(p => new { p.RevendaId, p.Id });
        Property(p => p.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
        HasRequired(p => p.Revenda).WithMany(r => r.Clientes).HasForeignKey(p => p.RevendaId);
    }
}

public class EmpresaMap : EntityTypeConfiguration<Empresa>
{
    public EmpresaMap()
    {
        HasKey(p => new { p.RevendaId, p.ClienteRevendaId, p.Id });
        Property(p => p.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
        HasRequired(p => p.Revenda).WithMany( ??? ).HasForeignKey(p => p.RevendaId);
        HasRequired(p => p.ClienteRevenda).WithMany(cr => cr.Empresas).HasForeignKey(p => p.ClienteRevendaId);
    }
}

public class ClienteEmpresaMap : EntityTypeConfiguration<ClienteEmpresa>
{
    public ClienteEmpresaMap()
    {
        HasKey(p => new { p.RevendaId, p.ClienteRevendaId, p.EmpresaId, p.Id });
        Property(p => p.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
        HasRequired(p => p.Revenda).WithMany( ??? ).HasForeignKey(p => p.RevendaId);
        HasRequired(p => p.ClienteRevenda).WithMany( ??? ).HasForeignKey(p => p.ClienteRevendaId);
        HasRequired(p => p.Empresa).WithMany(e => e.ClientesEmpresa).HasForeignKey(p => p.EmpresaId);
    }
}

Note in the last two mapping classes that they have the method WithMany( ??? ). My question whether the class Revenda should have a navigation Property for each of the other classes and also asking on account of these other classes and methods. What should be informed in them?

Either the class structure is wrong, or the mapping is wrong...?

  • The idea would be to have composite keys?

  • Yes, by data structure/security, right?

  • I even asked if this was a right way to write the model. I don’t know, maybe it wouldn’t be necessary to make composite keys, but just leave the Id of each class as primary key, the remainder make only foreign key mandatory.. but it is about the mapping rules for the EF that I have more doubt.

  • 1

    Could you edit the question and explain why that relationship? Because if you were to make the composite key, you could do so by Fluent: modelBuilder.Entity<Revenda>().HasKey(r => new { r.RevendaId, r.Id });

  • The bigger question is not the keys, I can even remove the keys. The keys I put to further complicate the question of the necessity of navigation properties. Because, since there is insertion of first class property in the latter, increased the need for navigation properties. So, the question is whether I need to exit by inserting browsing property of all layers? As an example, if I need to enter ICollection<ClienteEmpresa> in class Revenda ?

  • No no... For example... who will have navigation property of ClienteEmpresa will only be company

  • That’s what I wanted to know, which was something that @Ciganomorrisonmendez found strange in my previous question. Because since my class Revenda no navigational property for class ClienteEmpresa, therefore, in my class ClienteRevendaMap this assignment HasRequired(p => p.Revenda).WithMany().HasForeignKey(p => p.RevendaId); would be right, right???

  • I did not understand the relationship between the entities. A Revenda has n ClienteEmpresa? A company has n ClienteEmpresa? One Empresa has n ClienteEmpresa?

  • So @Ciganomorrisonmendez, my problem was to learn about how to build relationships for EF, mainly about navigation properties for the correct understanding of EF. And the example I set created this whole node because of the composite primary keys, where I wondered then if because of the existence of these keys I would need to leave including navigation property from the first class, the Revenda.

  • @Tiago It’s quite wrong your approach. I’m trying to write an answer, but I don’t understand the relationship between the entities.

  • Let me try to bring to a real example. I develop software, my resellers have Customers ( ClienteRevenda , distributors, for example ), and these customers serve other companies ( Empresa , retail). Companies that own their customers ( ClienteEmpresa , final client). Putting this in a system that encompasses everything, for example, I would already start by having several resales in my database, soon made the separation of Customers from Resellers, Companies and Clients of the Companies forcing the integrity of the data with compound key fields.

Show 6 more comments

3 answers

4


The co-relation between the properties should be something like this

//Revenda
public ICollection<ClienteRevenda> { get; set; }  //Navigation para os ClientesRevendas que possui

//ClienteRevenda
public Revenda Revenda { get; set; } //Navigation para a superior(que pertence)
public ICollection<Empresa> { get; set; }

//Empresa
public ClienteRevenda ClienteRevenda { get; set; } //Navigation para a superior
public ICollection<ClienteEmpresa> { get; set; }

//ClienteEmpresa
public Empresa Empresa{ get; set; } //Navigation para a superior
  • Okay, that’s cleared 50% of my doubts. ;)

  • As best practices for EF, composite keys like this as I rode and what you have shown to be ideal is no longer good. Correct? You didn’t include property navigation to the top as I did by composite key accounts.

2

Chaves Compostas

The idea in no way improves the security or structure, even hindering the implementation of its Controllers and Views. All it takes is one Property as a primary key that other Id’s are accessible through the declared virtual classes. This use may even be considered a bad practice.

Modeling of Entities

public class Revenda
{
    [Key]
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int Id {get; set;}
    [Required]
    public string Nome {get; set;}

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

public class ClienteRevenda
{
    [Key]
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int Id {get; set;}
    public int RevendaId {get; set;}

    [Required]
    public string Nome {get; set;}

    public virtual Revenda Revenda {get; set;}

    public virtual ICollection<Empresa> Empresas {get; set;}
}

public class Empresa
{
    [Key]
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int Id {get; set;}
    public int ClienteRevendaId {get; set;}

    [Required]    
    public string Nome {get; set;}

    public virtual ClienteRevenda ClienteRevenda {get; set;}

    public virtual ICollection<ClienteEmpresa> ClientesEmpresa {get; set;}
}

public class ClienteEmpresa
{
    [Key]
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int Id {get; set;}
    public int EmpresaId {get; set;}

    [Required]
    public string Nome {get; set;}

    public virtual Empresa Empresa {get; set;}
}

Mappings

public class RevendaMap : EntityTypeConfiguration<Revenda>
{
    public RevendaMap()
    {
        HasKey(p => p.Id);
        Property(p => p.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
    }
}

public class ClienteRevendaMap : EntityTypeConfiguration<ClienteRevenda>
{
    public ClienteRevendaMap()
    {
        HasKey(p => p.Id);
        Property(p => p.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
        HasRequired(p => p.Revenda).WithMany(r => r.Clientes).HasForeignKey(p => p.RevendaId);
    }
}

public class EmpresaMap : EntityTypeConfiguration<Empresa>
{
    public EmpresaMap()
    {
        HasKey(p => p.Id);
        Property(p => p.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
        HasRequired(p => p.ClienteRevenda).WithMany(cr => cr.Empresas).HasForeignKey(p => p.ClienteRevendaId);
    }
}

public class ClienteEmpresaMap : EntityTypeConfiguration<ClienteEmpresa>
{
    public ClienteEmpresaMap()
    {
        HasKey(p => new p.Id);
        Property(p => p.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
        HasRequired(p => p.Empresa).WithMany(e => e.ClientesEmpresa).HasForeignKey(p => p.EmpresaId);
    }
}

1

@James has not to do with the answer itself of the question, but in another ready that you should pay attention to it, it is the way will work the load of these dependencies...

For example, if you need to access the first ClienteEmpresa of his first Empresa of his first ClienteRevenda of his Revenda in a foreach loop, it would be something more or less like this (in the most common say way of doing).

foreach(var item in ctx.Revendas) {
    var nome = item.Revenda
    .ClienteRevendas().FirstOrDefault()
    .Empresas().FirstOrDefault()
    .ClienteEmpresas().FirstOrDefault().Nome;
    //faz alguma coisa por exemplo
}

Depending on how your list is loaded, it can become very, very expensive for the system. Because for every .ClienteRevendas() and .Empresas() He’ll have to go back and forth from the server all the time.

There are several ways to load, you can give a Include or do the Linq to bring a dry consultation tbm...

In that link have some good practices when working with Entity, take a special look at Items 5,7,8 that involve well what I would like to spend here.

Browser other questions tagged

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