次のコードの(サンプル)アプリケーションがあります。
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のどちらに進む必要がありますか?
さらに、WithOptionalDependent
とWithOptionalPrincipal
の実際の違いは何ですか? -MSDNで読みましたが、違いがよくわかりません。
おそらく、2つの1対1の関係をoptional:requiredとして作成しようとします。これは、Poll
mustには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(...)]
属性)を使用することもできます。
次の理由で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; }
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
他に役立つかもしれない何か。 [必須]属性を使用して外部キー属性(注釈)を設定すると、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;
}