web-dev-qa-db-ja.com

Entity FrameworkのFluentAPI /データアノテーションで外部キーのオプションの関係を定義するにはどうすればよいですか?

次のコードの(サンプル)アプリケーションがあります。

public class Posts
{

    [Key]
    [Required]
    public int ID { get; set; }

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

    public int PollID { get; set; }
    public virtual Poll Poll { get; set; }

    public int PostID { get; set; }
    public virtual Post Post { get; set; }

}

基本的に、これを行うより良い方法があるかどうかはわかりませんが、投稿のリストがあり、人々はそれがPollであるかPostであるかを選択できます。 Entity FrameworkはEnumsでは機能しないため、TypeOfPostに文字列として格納し、アプリケーションでTypeOfPostの値に基づいてプログラムでPollまたはPostにクエリを実行します。

とにかく「1つだけ必要」などと設定することはないと思いますので、アプリ内で全てのチェックやものを扱っています。 (誰かがより良い方法を知っているなら、言ってください!).

とにかく、問題は、SQL Management Studioに移動し、スキーマを手動で編集してnullを許可することでこれを正常に機能させることができます。

私は次の両方を試しました:

modelBuilder.Entity<Post>()
    .HasOptional(x => x.Poll).WithOptionalDependent();

modelBuilder.Entity<Post>()
    .HasOptional(x => x.Poll).WithOptionalPrincipal();

最初のものはデータベースにnullを許可する追加の列を作成するようで、2番目のものは何もしないようです。

最初のものは必要なものだと思いますが、Postクラスの[ForeignKey]と組み合わせて使用​​する必要があります。私がここで正しい場合、[ForeignKey]は仮想プロパティまたはプロパティのIDのどちらに進む必要がありますか?

さらに、WithOptionalDependentWithOptionalPrincipalの実際の違いは何ですか? -MSDNで読みましたが、違いがよくわかりません。

23
Wil

おそらく、2つの1対1の関係をoptional:requiredとして作成しようとします。これは、PollmustにはPostsへの参照があり、PostにもmustにはPostsへの参照が必要です。

_modelBuilder.Entity<Posts>()
    .HasOptional(x => x.Post)
    .WithRequired();

modelBuilder.Entity<Posts>()
    .HasOptional(x => x.Poll)
    .WithRequired();
_

これにより、Postsが自動的に関係のプリンシパルになり、PostまたはPollが依存関係になります。プリンシパルには関係の主キーがあり、依存関係のある外部キーはPost/Pollテーブルで同時に主キーでもあります。これは、1対1の関係だからです。 。 1対多の関係でのみ、外部キー用の個別の列が作成されます。 1対1の関係の場合、PostIdは主キーを介してPollIdを参照するため、外部キー列PostsおよびPostも削除する必要があります。およびPoll

モデルで適切と思われる代替アプローチは、継承マッピングです。次に、モデルは次のようになります。

_public abstract class BasePost  // your former Posts class
{
    public int ID { get; set; }
    public string UserName { get; set; }
}

public class Post : BasePost
{
    public string Text { get; set; }
    // other properties of the Post class
}

public class Poll : BasePost
{
    // properties of the Poll class
}
_

TypeOfPost LINQ演算子を使用して2つの具象型をフィルタリングできるため、たとえば、次のようにOfTypeは必要ありません。

_var x = context.BasePosts.OfType<Post>()
    .Where(p => p.UserName == "Jim")
    .ToList();
_

これにより、特定のユーザーのすべての投稿が選択されますが、投票は選択されません。

次に、使用する継承マッピングの種類を決定する必要があります- TPH、TPTまたはTPC

編集

1対多の関係を取得するには、Fluent APIで次のマッピングを指定できます。

_modelBuilder.Entity<Posts>()
    .HasOptional(x => x.Post)
    .WithMany()
    .HasForeignKey(x => x.PostID);

modelBuilder.Entity<Posts>()
    .HasOptional(x => x.Poll)
    .WithMany()
    .HasForeignKey(x => x.PollID);
_

あなたがすでに見つけたように、これのために外部キーのプロパティはnullable(_int?_)でなければなりません。外部キープロパティの命名は、EFがマッピングに使用する命名規則に従うため、Fluentマッピングを完全に省略できます。これは、(PostFKなどの)型破りな名前を使用している場合にのみ必要です。次に、Fluent APIの代わりにデータ注釈([ForeignKey(...)]属性)を使用することもできます。

15
Slauma

次の理由でnullを許可しなかった理由:

public int PollID { get; set; }
public virtual Poll Poll { get; set; }

public int PostID { get; set; }
public virtual Post Post { get; set; }

になるはずだった

public int? PollID { get; set; }
public virtual Poll Poll { get; set; }

public int? PostID { get; set; }
public virtual Post Post { get; set; }
20
Wil

ForeignKeyは、それをオプションにするためにnull可能である必要があります-仮想は別個であり、遅延読み込みでのみ必要です。

宣言型EFコードファーストで必要な関係:

public User User { get; set; }
[ForeignKey("User")]
public int UserId { get; set; }

宣言型EFコードファーストのオプションの関係:

public User User { get; set; }
[ForeignKey("User")]
public int? UserId { get; set; }

update-database -verbose -fを実行すると表示されます。

ALTER TABLE [dbo].[MyTable] ALTER COLUMN [UserId] [int] NULL
8
Chris Moschini

他に役立つかもしれない何か。 [必須]属性を使用して外部キー属性(注釈)を設定すると、FKプロパティがnull値であっても、EF必須のナビゲーションプロパティが適用されます。 FKプロパティが必要なレガシーデータの特別なケースがありますが、関係のレコードを参照する場合としない場合があります。理にかなっていますが、EFがこれほど「スマート」であるとは思いませんでした。

    [Required] <-- even if the FK is nullable, OrgUnit will be Required
    [StringLength(10)]
    [ForeignKey("OrgUnit"), Column(Order = 1)]
    public string OrgCode
    {
        get;
        set;
    }

    ... other FK, Column order 0   

    public virtual OrgUnit OrgUnit
    {
        get;
        set;
    }
0
Michael K