3

Pour résumer mon modèle:clé étrangère comme discriminateur TPH à EF4 en utilisant le code CTP4 Première

  • Licence et certificat sont des enfants de qualification
  • Une qualification a une et une seule profession
  • Une profession est soit une sorte sous licence (type 1) ou d'un type certifié (type 2)

Exigence: Représentent les relations entre entiti d'affaires sans introduire de redondance dans le schéma de base de données. Le type de qualification (licence/certificat) doit correspondre au type de profession.

Voici mon modèle simplifié tel qu'il est actuellement - je vous expliquer pourquoi cela ne fonctionne pas au-dessous:

Public Class Profession 
    <Key()> 
    <DataMember(Order:=0)> 
    Public Property Type As Integer 
    <Key()> 
    <DataMember(Order:=1)> 
    Public Property Code As String 

    Public Property Title As String 
End Class 

Public Class Qualification 
    Public Property Id As Integer 
    Public Property PersonId As Integer 
    Public Property Type As Integer 
    Public Property ProfessionCode As String 
    Public Overridable Property Person As Person 
    Public Overridable Property Profession As Profession 
End Class 

Public Class License 
    Inherits Qualification 

    Public Property Number As String   
End Class 

Public Class Certificate 
    Inherits Qualification 

    Public Property IssuerName As String  
End Class 

Voici le ModelBuilder simplifié:

modelBuilder.Entity(Of Qualification) _ 
    .Property(Function(q) q.ProfessionCode).IsRequired() 

modelBuilder.Entity(Of Qualification) _ 
    .HasRequired(Of Profession)(Function(q) q.Profession) _ 
    .HasConstraint(Function(q, p) p.Type = q.Type AndAlso p.Code = q.ProfessionCode) 

modelBuilder.Entity(Of Qualification) _ 
    .MapHierarchy() _ 
    .Case(Of Qualification)(Function(q) New With { 
     q.Id, 
     q.PersonId, 
     q.ProfessionCode, 
     .Type = 0) _ 
    .Case(Of License)(Function(q) New With { 
     q.Number, 
     .Type = 1}) _ 
    .Case(Of Certificate)(Function(q) New With { 
     q.IssuerName, 
     .Type = 2}) _ 
    .ToTable("dbo.Qualifications") 

La raison pour laquelle cela ne travail est que EF4 does not allow propriétés FK à doubler en tant que discriminateur TPH. Cela signifie que le type ne peut pas être à la fois un discriminateur et un champ de clé étrangère. Essayer de coder en dur Profession La saisie de la méthode HasConstraint pour chaque entité ne fonctionne pas non plus - cela génère une exception.

Une solution possible serait d'ajouter une clé de substitution à Profession, de se débarrasser de la propriété Type dans Qualification et de la remplacer par ProfessionId FK. Cela supprimerait la préoccupation de redondance, mais détruirait aussi TPH. En effet, le discriminateur passe de Qualification à Profession. Le problème ici est que je n'ai pas trouvé un moyen de cartographier les objets Licence et Certificat. Peut-être que je peux mapper à des vues à la place? Mais comment puis-je faire ça dans Code First? Donc, maintenant, je suis confronté à un certain nombre de choix peu recommandables. Aucune suggestion?

Répondre

1

j'ai réussi à le faire fonctionne en changeant à ce modèle:

public class Profession {  
    [Key][DataMember(Order = 0)]  
    public int Type { get; set; } 
    [Key][DataMember(Order = 1)] 
    public string Code { get; set; } 
    public string Title { get; set; } 
} 

public class Qualification { 
    public int Id { get; set; }    
    [Required] 
    public int ProfessionType { get; set; } 
    [Required] 
    public string ProfessionCode { get; set; }     
    [Required] 
    public virtual Profession Profession { get; set; } 
} 

public class License : Qualification { 
    public string Number { get; set; } 
} 

public class Certificate : Qualification { 
    public string IssuerName { get; set; } 
} 

class Context : DbContext { 
    public DbSet<Qualification> Qualifications { get; set; } 
    public DbSet<Profession> Professions { get; set; } 

    protected override void OnModelCreating(ModelBuilder modelBuilder) { 
     modelBuilder.Entity<Qualification>() 
      .HasRequired<Profession>(q => q.Profession) 
      .HasConstraint((q, p) => q.ProfessionCode == p.Code 
            && q.ProfessionType == p.Type); 

     modelBuilder.Entity<Qualification>().MapHierarchy() 
      .Case<Qualification>(q => new { 
       q.ProfessionCode, 
       q.ProfessionType, 
       q.Id,      
       Type = 0 
      }).Case<License>(q => new { 
       q.Number, 
       Type = 1 
      }).Case<Certificate>(q => new { 
       q.IssuerName, 
       Type = 2 
      }).ToTable("Qualifications"); 
    } 
} 

Cependant, comme vous pouvez le dire, ProfessionType est redondant sur la qualification, et il n'y a pas moyen de le contourner car comme vous l'avez dit, EF ne vous permettra de réutiliser un discriminateur comme FK qui font sens depuis cette règle:

une profession est soit une sorte sous licence (type 1) ou une sorte certifié (type 2)

est quelque chose que EF n'est pas au courant donc il doit l'empêcher afin de protéger la hierachie.

Personnellement, je concevoir le modèle d'objet comme suit que je pense est plus claire et moins redondant:

public class Profession { 
    public int ProfessionId { get; set; }   
    public int Type { get; set; } 
    public string Code { get; set; } 
    public string Title { get; set; } 
} 

public class Qualification { 
    public int Id { get; set; } 
    public int ProfessionId { get; set; }     
    [Required] 
    public virtual Profession Profession { get; set; } 
} 

public class License : Qualification { 
    public string Number { get; set; } 
} 

public class Certificate : Qualification { 
    public string IssuerName { get; set; } 
} 

class Context : DbContext { 
    public DbSet<Qualification> Qualifications { get; set; } 
    public DbSet<Profession> Professions { get; set; } 

    protected override void OnModelCreating(ModelBuilder modelBuilder) { 
     modelBuilder.Entity<Qualification>() 
      .HasRequired<Profession>(q => q.Profession) 
      .HasConstraint((q, p) => q.ProfessionId == p.ProfessionId); 

     modelBuilder.Entity<Qualification>().MapHierarchy() 
      .Case<Qualification>(q => new { 
       q.ProfessionId,     
       q.Id,      
       Type = 0 
      }) 
      .Case<License>(q => new { 
       q.Number, 
       Type = 1 
      }) 
      .Case<Certificate>(q => new { 
       q.IssuerName, 
       Type = 2 
      }) 
      .ToTable("Qualifications"); 
    } 
} 

Quels sont les résultats du schéma suivant dans le DB: alt text

Encore une autre façon de éviter DRY serait de transformer la hiérarchie pour être TPT au lieu de TPH:

public class Profession { 
    [Key] 
    [DataMember(Order = 0)] 
    public int Type { get; set; } 
    [Key] 
    [DataMember(Order = 1)] 
    public string Code { get; set; } 
    public string Title { get; set; } 
} 

public class Qualification { 
    public int Id { get; set; } 
    [Required] 
    public int ProfessionType { get; set; } 
    [Required] 
    public string ProfessionCode { get; set; } 
    [Required] 
    public virtual Profession Profession { get; set; } 
} 

public class License : Qualification { 
    public string Number { get; set; } 
} 

public class Certificate : Qualification { 
    public string IssuerName { get; set; } 
} 

class Context : DbContext 
{ 
    public DbSet<Qualification> Qualifications { get; set; } 
    public DbSet<Profession> Professions { get; set; } 

    protected override void OnModelCreating(ModelBuilder modelBuilder) { 
     modelBuilder.Entity<Qualification>() 
      .HasRequired<Profession>(q => q.Profession) 
      .HasConstraint((q, p) => q.ProfessionCode == p.Code 
            && q.ProfessionType == p.Type); 

     modelBuilder.Entity<Qualification>().MapHierarchy(q => new 
     { 
      q.Id, 
      q.ProfessionCode, 
      q.ProfessionType, 
     }) 
     .ToTable("Qualifications"); 

     modelBuilder.Entity<License>().MapHierarchy(l => new 
     { 
      l.Id, 
      l.Number 
     }) 
     .ToTable("Licenses"); 

     modelBuilder.Entity<Certificate>().MapHierarchy(c => new 
     { 
      c.Id, 
      c.IssuerName 
     }) 
     .ToTable("Certificates"); 
    } 
} 


Quels sont les résultats du schéma suivant dans DB:

alt text

+0

@Morteza, Merci pour votre contribution. J'espérais qu'il pourrait y avoir une meilleure façon de ne pas violer DRY. Comme vous le savez, Type est redondant dans cette solution. Souhaitez-vous simplement essayer de contrôler cela par le biais de règles métier? – Antony

+0

En * contrôlant cela par le biais des règles métier *, vous voulez dire ne pas avoir de colonne de discriminateur et regarder seulement dans ProfessionType dans le tableau Qualification et déterminer quel type d'objet vous avez affaire, correct? –

+0

J'ai ajouté une autre façon d'éviter DRY à ma réponse: TPT au lieu de TPH. –

Questions connexes