How to create 1-N relationship in class with more than one property and even subtype?

4

I have the following class structure, unconventional but it's in the template I need to solve my problem:

Tree:

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

    [Required]
    [StringLength(50)]
    public string Descricao { get; set; }

    public virtual ICollection<Galho_TipoA> Galhos_TipoA { get; set; }
    public virtual ICollection<Galho_TipoB> Galhos_TipoB { get; set; }
}

Twig:

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

    [Required]
    public int ArvoreId { get; set; }

    [Required]
    [StringLength(50)]
    public string Descricao { get; set; }

    [ForeignKey("ArvoreId")]
    public Arvore Avore { get; set; }
}

public class Galho_TipoA : Galho { }
public class Galho_TipoB : Galho { }

When generating my schema I'm getting the following:

CreateTable(
    "dbo.Arvores",
    c => new
        {
            Id = c.Int(nullable: false, identity: true),
            Descricao = c.String(nullable: false, maxLength: 50),
        })
    .PrimaryKey(t => t.Id);

CreateTable(
    "dbo.Galhos",
    c => new
        {
            Id = c.Int(nullable: false, identity: true),
            ArvoreId = c.Int(nullable: false),
            Descricao = c.String(nullable: false, maxLength: 50),
            Discriminator = c.String(nullable: false, maxLength: 128),
            Arvore_Id = c.Int(),
            Arvore_Id1 = c.Int(),
        })
    .PrimaryKey(t => t.Id)
    .ForeignKey("dbo.Arvores", t => t.Arvore_Id)
    .ForeignKey("dbo.Arvores", t => t.Arvore_Id1)
    .ForeignKey("dbo.Arvores", t => t.ArvoreId, cascadeDelete: true)
    .Index(t => t.ArvoreId)
    .Index(t => t.Arvore_Id)
    .Index(t => t.Arvore_Id1);

Note that several indexes are being generated for Arvore properties that are of the same type:

.ForeignKey("dbo.Arvores", t => t.Arvore_Id)
.ForeignKey("dbo.Arvores", t => t.Arvore_Id1)
.ForeignKey("dbo.Arvores", t => t.ArvoreId, cascadeDelete: true)
.Index(t => t.ArvoreId)
.Index(t => t.Arvore_Id)
.Index(t => t.Arvore_Id1);

I created the types Galho_TipoA and Galho_TipoB exactly to differentiate the records by the Discriminator field, but it did not work. Instead of pointing to ArvoreId have created others, one for each property in Arvore .

How to resolve this issue to ArvoreId and not have to generate others?
Or, what would be the correct way to structure this schema? p>

Issue

The logic that the model needs to meet is as follows: A Arvore can have several Galhos of several types. So I thought of the types as separate properties.

    
asked by anonymous 18.08.2014 / 16:28

2 answers

3

The problem is not so much about class derivation, but about the fact that you have two ICollection whose base type is the same (in this case, Galho ). This creates some ambiguity at the time of the Entity Framework assembling the sentence, since it can perfectly popular a ICollection type A with type B data, even if there is Discriminator defined.

Apparently it is not a Design flaw, but a feature shortage of the Entity Framework itself. What you normally do is to accept the two columns and map them to better names using the Fluent API .

    
18.08.2014 / 18:43
2

Expected effect on the question is not good practice, but I followed the question:

[Table("Arvore")]
public class Arvore
{
    public Arvore() { }
    [Key]
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int ArvoreId { get; set; }

    [Required]
    [StringLength(50)]
    public string Descricao { get; set; }

}
public class ArvoreA : Arvore 
{
    public ArvoreA()
    {
        this.Galho = new HashSet<Galho>();
    }
    [ForeignKey("ArvoreId")]
    public virtual ICollection<Galho> Galho { get; set; }
}
public class ArvoreB : Arvore 
{
    public ArvoreB()
    {
        this.Galho = new HashSet<Galho>();
    }
    [ForeignKey("ArvoreId")]
    public virtual ICollection<Galho> Galho { get; set; }
}
[Table("Galho")]
public class Galho
{
    [Key]
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int GalhoId { get; set; }        

    [Required]
    [StringLength(50)]
    public string Descricao { get; set; }

    [Required]
    public int ArvoreId { get; set; }

}
public class GalhoA : Galho 
{        
    [ForeignKey("ArvoreId")]
    public virtual Arvore Arvore { get; set; }
}
public class GalhoB : Galho 
{        
    [ForeignKey("ArvoreId")]
    public virtual Arvore Arvore { get; set; }
}
public class Context : DbContext
{
    public Context()
        :base("Con") {
            this.Configuration.ProxyCreationEnabled = true;
    }
    public DbSet<Arvore> Arvore { get; set; }
    public DbSet<Galho> Galho { get; set; }

}
using (Context ctx = new Context())
{
    //ArvoreA arvore = new ArvoreA();
    //arvore.Descricao = "Arvore 3";                
    //arvore.Galho.Add(new GalhoA() { Descricao = "Galho 3" });                
    //ctx.Arvore.Add(arvore);

    //ArvoreA arvore = ctx.Arvore.OfType<ArvoreA>().Where(x => x.ArvoreId == 1).FirstOrDefault();
    //arvore.Galho.Add(new GalhoA() { Descricao = "Galho 1" });

    ctx.SaveChanges();
}
    
18.08.2014 / 18:18