3

私のモデルを要約するには:

  • ライセンスと証明書は資格の子です
  • Qualification には 1 つだけの専門職があります
  • 職業は、認可された種類 (タイプ 1) または認定された種類 (タイプ 2) のいずれかです。

要件: データベース スキーマに冗長性を導入することなく、ビジネス エンティティ間の関係を表現します。資格の種類 (ライセンス/証明書) は、職業の種類と一致する必要があります。

これが現在の私の簡略化されたモデルです。これが機能しない理由を以下で説明します。

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

簡略化した ModelBuilder は次のとおりです。

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")

これが機能しない理由は、EF4がFK プロパティを TPH 識別子として兼用することを許可していないためです。これは、Type を識別子と外部キー フィールドの両方にすることはできないことを意味します。各エンティティの HasConstraint メソッドで職業タイプをハードコーディングしようとしても機能しません。これにより、例外が生成されます。

可能な解決策は、Profession に代理キーを追加し、Qualification の Type プロパティを取り除き、ProfessionId FK に置き換えることです。これにより、冗長性の懸念が取り除かれますが、TPH も破壊されます。実際には、弁別子は資格から専門職に移動します。ここでの問題は、License オブジェクトと Certificate オブジェクトをマップする方法がわからなかったことです。代わりにビューにマップできますか?しかし、Code First でそれを行うにはどうすればよいでしょうか。

ですから、今、私は多くの好ましくない選択に直面しています。助言がありますか?

4

1 に答える 1

1

このモデルに変更することで、なんとか機能させることができました:

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");
    }
}

ただし、おわかりのように、ProfessionType は Qualification では冗長であり、回避する方法はありません。あなたが言ったように、EF では、このルール以降、意味のある FK として識別子を再利用することはできません。

職業は、認可された種類 (タイプ 1) または認定された種類 (タイプ 2) のいずれかです。

EFは認識していないため、階層を保護するために防止する必要があります。

個人的には、オブジェクト モデルを次のように設計します。これは、より明確で冗長性が低いと思います。

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");
    }
}

DB で次のスキーマが生成されます。 代替テキスト

DRY を回避するさらに別の方法は、階層を TPH ではなく TPT にすることです。

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");
    }
}


DB で次のスキーマが生成されます。

代替テキスト

于 2010-11-20T03:28:15.373 に答える