web-dev-qa-db-ja.com

Entity Framework:Discriminator列をテーブルから回避する方法は?

Entity Framework Code Firstアプローチを使用して作成された次の表があります。

  1. 不要なDiscriminator列がデータベースに作成されないようにC#コードを変更するにはどうすればよいですか?それを達成するための属性はありますか?
  2. 外部キー列名を「Payment_ PaymentID」ではなく「PaymentID」として作成する方法は?それを達成するための属性はありますか?

注:EntityFramework.dllのランタイムバージョンはv4.0.30XXXです

enter image description here

[〜#〜] code [〜#〜]

public abstract class PaymentComponent
{
    public int PaymentComponentID { get; set; }
    public int MyValue { get; set; }
    public string MyType { get; set; }
    public abstract int GetEffectiveValue();
}


public partial class GiftCouponPayment : PaymentComponent
{

    public override int GetEffectiveValue()
    {
        if (MyValue < 2000)
        {
            return 0;
        }
        return MyValue;
    }

}


public partial class ClubCardPayment : PaymentComponent
{
    public override int GetEffectiveValue()
    {
        return MyValue;
    }
}

public partial class Payment
{
    public int PaymentID { get; set; }
    public List<PaymentComponent> PaymentComponents { get; set; }
    public DateTime PayedTime { get; set; }

}



//System.Data.Entity.DbContext is from EntityFramework.dll
public class NerdDinners : System.Data.Entity.DbContext
{

    public NerdDinners(string connString): base(connString)
    { 

    }

    protected override void OnModelCreating(DbModelBuilder modelbuilder)
    {
        modelbuilder.Conventions.Remove<PluralizingTableNameConvention>();
    }


    public DbSet<GiftCouponPayment> GiftCouponPayments { get; set; }
    public DbSet<ClubCardPayment> ClubCardPayments { get; set; }
    public DbSet<Payment> Payments { get; set; }

}

[〜#〜] client [〜#〜]

    static void Main(string[] args)
    {

        string connectionstring = "Data Source=.;Initial Catalog=NerdDinners;Integrated Security=True;Connect Timeout=30";

        using (var db = new NerdDinners(connectionstring))
        {

            GiftCouponPayment giftCouponPayment = new GiftCouponPayment();
            giftCouponPayment.MyValue=250;
            giftCouponPayment.MyType = "GiftCouponPayment";

            ClubCardPayment clubCardPayment = new ClubCardPayment();
            clubCardPayment.MyValue = 5000;
            clubCardPayment.MyType = "ClubCardPayment";


            List<PaymentComponent> comps = new List<PaymentComponent>();
            comps.Add(giftCouponPayment);
            comps.Add(clubCardPayment);

            var payment = new Payment { PaymentComponents = comps, PayedTime=DateTime.Now };
            db.Payments.Add(payment);

            int recordsAffected = db.SaveChanges();


        }

    }
26
Lijo

TPH継承には、エンティティのタイプを識別するために使用される特別な列が必要です。デフォルトでは、この列はDiscriminatorと呼ばれ、派生エンティティの名前が含まれています。 Fluent-APIを使用して、異なる列名と異なる値を定義できます。 MyType列は実際には弁別子であるため、直接使用することもできますが、そのような場合、エンティティにその列を含めることはできません(列は1回しかマッピングできず、弁別子として使用する場合は既にマッピングと見なされます)。

外部キー列の名前は、Fluent-APIで再び制御できます。

protected override void OnModelCreating(DbModelBuilder modelbuilder)
{
    modelbuilder.Conventions.Remove<PluralizingTableNameConvention>();

    // Example of controlling TPH iheritance:
    modelBuilder.Entity<PaymentComponent>()
            .Map<GiftPaymentComponent>(m => m.Requires("MyType").HasValue("G"))
            .Map<ClubPaymentComponent>(m => m.Requires("MyType").HasValue("C"));

    // Example of controlling Foreign key:
    modelBuilder.Entity<Payment>()
                .HasMany(p => p.PaymentComponents)
                .WithRequired()
                .Map(m => m.MapKey("PaymentId"));
}
31
Ladislav Mrnka

プロパティが列にマップされない場合、属性[NotMapped]を追加します。

8
John Li

タイプごとのテーブル(TPT)も使用できます。

http://weblogs.asp.net/manavi/inheritance-mapping-strategies-with-entity-framework-code-first-ctp5-part-2-table-per-type-tpt

タイプごとのテーブル(TPT)

タイプごとのテーブルは、継承関係をリレーショナル外部キーの関連付けとして表すことに関するものです。抽象クラスを含む永続プロパティを宣言するすべてのクラス/サブクラスには、独自のテーブルがあります。サブクラスのテーブルには、基本クラステーブルの外部キーでもある主キーとともに、継承されない各プロパティ(サブクラス自体によって宣言された各プロパティ)の列のみが含まれます。

EF Code FirstでTPTを実装する

サブクラスにTable属性を配置して、マッピングされたテーブル名を指定するだけでTPTマッピングを作成できます(Table属性は新しいデータアノテーションであり、CTP5のSystem.ComponentModel.DataAnnotations名前空間に追加されました。

流れるようなAPIを好む場合は、ToTable()メソッドを使用してTPTマッピングを作成できます。

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    modelBuilder.Entity<BankAccount>().ToTable("BankAccounts");
    modelBuilder.Entity<CreditCard>().ToTable("CreditCards");
}
3
user3285954

サブクラスを使用しているため、サブクラスの各タイプを区別するためにDiscriminator列が必要です。

1
mathieu

「GiftCouponPayment」と「ClubCardPayment」の両方が「PaymentComponent」から派生するため、EFは個別のテーブルを使用せず、その列が必要になります。別の動作が必要な場合は、デフォルトのテーブルアクセスをオーバーライドし、フィールドをクラスにマップする必要があります(実行したくないと思います)これに簡単な方法があるかどうかはわかりません。エンティティから最初に、テーブルを作成するテンプレートを介した方法があることを知っています。
外部キー列名も同じです。 EFは、この方法を使用してキー/外部キー名の名前を作成します。あなたが好きなようにテーブルをフォーマットしたい場合、あなたはそれをすべて自分で行う必要があり、それはなぜEFを使用するのかという疑問につながります。
化粧品以外にそうする理由はありますか?

0
DerApe

テーブルのDiscriminator列を回避するには、派生クラスに注釈[NotMapped]を追加するだけです。

0
Valeriy

Discriminator列を削除し、代わりにDiscriminatorとしてPaymentIdという名前の列を取得するサンプルコード。したがって、両方の質問を解決します。 MicrosoftのFluent Apiのオリジナルドキュメントに基づいています。

https://msdn.Microsoft.com/en-us/library/jj591617%28v=vs.113%29.aspx?f=255&MSPPError=-2147217396

public enum MyEnum
{ 
    Value1, Value2
}

public class MyBaseClass

{ 
    [NotMapped]
    public MyEnum PaymentId { get; protected set; }
}

public class DerivedOne: MyBaseClass
{
    public DerivedOne()
    {
        PaymentId = MyEnum.Value1;
    } 
}

public class DerivedTwo: MyBaseClass
{
    public DerivedTwo()
    {
        PaymentId = MyEnum.Value2;
    }
}

public class MyDbContext : DbContext
{
    DbSet<MyBaseClass> MyBaseClass { get; set; }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);

        modelBuilder.Entity<MyBaseClass>()
            .Map<DerivedOne>(x => x.Requires("PaymentId").HasValue((int)PaymentId.Value1))
            .Map<DerivedTwo>(x => x.Requires("PaymentId").HasValue((int)PaymentId.Value2));
    }
}
0
Ogglas