Entity Framework 4 Code First(POCO)を使用して1対1の関係を宣言するにはどうすればよいですか?
この質問(Entity Framework 4の1対1の関係) が見つかりましたが、回答が参照している記事は役に立ちませんでした(1-1の関係であるコードが1行ありますが、それを定義する方法についての言及はありません)。
あなたはこのようなものを探していますか?
_public class User
{
public int Id { get; set; }
public string Username { get; set; }
public Profile Profile { get; set; }
public int ProfileId { get; set; }
}
public class Profile
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string PostalCode { get; set; }
// etc...
}
public class UserMapping : EntityConfiguration<User>
{
public UserMapping()
{
this.HasKey(u => u.Id);
this.Property(u => u.Username).HasMaxLength(32);
// User has ONE profile.
this.HasRequired(u => u.Profile);
}
}
public class ProfileMapping : EntityConfiguration<Profile>
{
public ProfileMapping()
{
this.HasKey(p => p.Id);
this.Property(p => p.FirstName).HasMaxLength(32);
this.Property(p => p.LastName).HasMaxLength(32);
this.Property(p => p.PostalCode).HasMaxLength(6);
}
}
_
[〜#〜] edit [〜#〜]:ええ、私の前にVSはありませんでしたが、現在の代わりにUserMapping
に次の行を追加する必要がありますHasRequired
そしてProfileId
プロパティも追加します(追加した_Profile_Id
_の代わりに):
_this.HasRequired(u => u.Profile).HasConstraint((u, p) => u.ProfileId == p.Id);
_
現在、これを回避する方法はないと思いますが、CTP4のみであるため、変更されると確信しています。私が言うことができればそれは素晴らしいでしょう:
_this.HasRequired(u => u.Profile).WithSingle().Map(
new StoreForeignKeyName("ProfileId"));
_
この方法では、ProfileId
プロパティを含める必要はありません。たぶん、これを回避する方法が現在あり、私が考えるのはまだ早朝です:)。
また、「ナビゲーションプロパティ」を含める場合は、必ず.Include("Profile")
を呼び出してください。
3つの方法:
A)ナビゲーションプロパティを使用して両方のクラスを相互に宣言します。テーブルの1つ(従属テーブル)をその主キーのForeignKey属性でマークします。 EFはこれから1対1を推測します:
public class AppUser
{
public int Id { get; set; }
public string Username { get; set; }
public OpenIdInfo OpenIdInfo { get; set; }
}
public class OpenIdInfo
{
[ForeignKey("AppUser")]
public int Id { get; set; }
public string OpenId { get; set; }
public AppUser AppUser { get; set; }
}
私はvirtual
を使用しなかったので、使用しないでください。*
B)両方のテーブル名を明示的に指定して継承階層を宣言し、Table-Per-Typeと共有主キーを作成します。
using System.ComponentModel.DataAnnotations;
[Table("AppUser")]
public class AppUser
{
public int Id { get; set; }
public string Username { get; set; }
public OpenIdInfo OpenIdInfo { get; set; }
}
[Table("AdminUser")]
public class AdminUser : AppUser
{
public bool SuperAdmin { get; set; }
}
2つのテーブルを取得します。1つはAppUser用、もう1つはAdminUser用です。 AdminUserはAppUserと1:1であり、依存関係にあります。つまり、AdminUserを削除できますが、AdminUserがまだAppUserをポイントしているときにAppUserを削除すると、制約違反エラーが発生します。
C)EFで1対1を実行する方法は2つあります。
Entity-Splitting 、単一のクラスがありますが、それはプライマリテーブル、および1つ以上の1対1の関連テーブルに格納されます。
Table-Splitting 、オブジェクトのツリーが平らになって1つのテーブルになります。たとえば、Addressプロパティを持つクラスには、Address_CityなどのAddressオブジェクトの列が1つのテーブルにフラット化されます。
*任意のEFプロパティまたはコレクションに仮想を含めることができます 遅延ロードする場合 。これにより、遅延ロードされたプロパティを持つオブジェクトを、たとえばMVC JSONコンバーターやオブジェクト階層をウォークするその他のものに渡すと、無限ループが発生したり、DB全体がロードされたりする可能性があります。遅延読み込みは常に同期的に実行され、スレッドをブロックします。通知はありません。要約すると、コード、アプリ、またはサーバーをフリーズする方法のリストは長いです。 EFクラスで仮想を使用することは避けてください。はい、インターネット上にはそれを使用するコードサンプルがたくさんあります。いいえ、まだ使用しないでください。
public class User
{
public int Id { get; set; }
public string Username { get; set; }
public virtual Profile Profile { get; set; }
}
public class Profile
{
public int Id { get; set; }
public int UserID { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string PostalCode { get; set; }
}
仮想プロファイルとユーザーIDを追加すると、そこに到達するはずです。
次のStudentエンティティとStudentAddressエンティティの例を見てください。
DataAnnotationsを使用して1対0または1の関係を構成します:
public class Student
{
public Student() { }
public int StudentId { get; set; }
public string StudentName { get; set; }
public virtual StudentAddress Address { get; set; }
}
public class StudentAddress
{
[ForeignKey("Student")]
public int StudentAddressId { get; set; }
public string Address1 { get; set; }
public string Address2 { get; set; }
public string City { get; set; }
public int Zipcode { get; set; }
public string State { get; set; }
public string Country { get; set; }
public virtual Student Student { get; set; }
}
StudentAddressエンティティが規則に従わない場合:
たとえば、StudentAddressエンティティがPKの規則に従わない場合、つまりIdプロパティの名前が異なる場合は、PK用にも構成する必要があります。 StudentAddressIdの代わりにStudentIdというプロパティ名を持つ次のStudentAddressエンティティについて考えてみます。
public class Student
{
public Student() { }
public int StudentId { get; set; }
public string StudentName { get; set; }
public virtual StudentAddress Address { get; set; }
}
public class StudentAddress
{
[Key, ForeignKey("Student")]
public int StudentId { get; set; }
public string Address1 { get; set; }
public string Address2 { get; set; }
public string City { get; set; }
public int Zipcode { get; set; }
public string State { get; set; }
public string Country { get; set; }
public virtual Student Student { get; set; }
}
上記の例では、StudentIdプロパティをKeyおよびForeignKeyとして構成する必要があります。これにより、StudentAddressエンティティのStudentIdプロパティがPKとFKの両方になります。
Fluent APIを使用して1対ゼロまたは1の関係を構成します:
StudentおよびStudentAddressが規則に従う場合:StudentおよびStudentAddressエンティティは、PrimaryKeyのデフォルトのコードファースト規則に従います。したがって、PrimaryKeysを定義するためにそれらを構成する必要はありません。 StudentAddressIdがForeignKeyであるStudentAddressエンティティを構成するだけで済みます。
次の例では、Fluent APIを使用して、StudentとStudentAddressの間に1対0または1の関係を設定します。
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
// Configure Student & StudentAddress entity
modelBuilder.Entity<Student>()
.HasOptional(s => s.Address) // Mark Address property optional in Student entity
.WithRequired(ad => ad.Student); // mark Student property as required in StudentAddress entity. Cannot save StudentAddress without Student
}
上記の例では、StudentエンティティはHasOptional()メソッドを使用して構成されています。これは、StudentエンティティのStudentAddressナビゲーションプロパティがオプションであることを示します(Studentエンティティを保存する場合は不要です)。次に、WithRequired()メソッドはStudentAddressエンティティを構成し、必要に応じてStudentAddressのStudentナビゲーションプロパティを作成します(StudentAddressエンティティを保存するときに必要です。StudentAddressエンティティがStudentナビゲーションプロパティなしで保存している場合は例外がスローされます)。これにより、StudentAddressIdもForeignKeyになります。
したがって、StudentAddressオブジェクトをアタッチせずにStudentエンティティを保存できるが、StudentエンティティのオブジェクトをアタッチせずにStudentAddressエンティティを保存できない、2つのエンティティ間の1対ゼロまたは1の関係を構成できます。これにより、一方の端が必要になります。
StudentAddressエンティティが規則に従わない場合:
ここで、StudentAddressエンティティの例を見てみましょう。ここでは、主キーの規則に従わない、つまりIdプロパティ名がIdとは異なります。次のStudentエンティティとStudentAddressエンティティについて考えてみます。
public class Student
{
public Student() { }
public int StudentId { get; set; }
public string StudentName { get; set; }
public virtual StudentAddress Address { get; set; }
}
public class StudentAddress
{
public int StudentId { get; set; }
public string Address1 { get; set; }
public string Address2 { get; set; }
public string City { get; set; }
public int Zipcode { get; set; }
public string State { get; set; }
public string Country { get; set; }
public virtual Student Student { get; set; }
}
したがって、次に示すように、StudentAddressのPrimaryKeyとForeignKeyのStudentAddressのStudentIdプロパティを構成する必要があります。
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
// Configure StudentId as PK for StudentAddress
modelBuilder.Entity<StudentAddress>()
.HasKey(e => e.StudentId);
// Configure StudentId as FK for StudentAddress
modelBuilder.Entity<Student>()
.HasOptional(s => s.Address)
.WithRequired(ad => ad.Student);
}
Fluent APIを使用して1対1の関係を構成します:
両端が必要なFluentAPIを使用して、エンティティ間の1対1の関係を構成できます。つまり、StudentエンティティオブジェクトにはStudentAddressエンティティオブジェクトが含まれ、StudentAddressエンティティにはStudentエンティティオブジェクトが含まれている必要があります。
注:MS SQL Serverでは、技術的に1対1の関係は不可能です。常に1対0または1になります。 EFは、DBにないエンティティで1対1の関係を形成します。
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
// Configure StudentId as PK for StudentAddress
modelBuilder.Entity<StudentAddress>()
.HasKey(e => e.StudentId);
// Configure StudentId as FK for StudentAddress
modelBuilder.Entity<Student>()
.HasRequired(s => s.Address)
.WithRequiredPrincipal(ad => ad.Student);
}
上記の例では、modelBuilder.Entity()。HasRequired(s => s.Address)により、StudentAddressのAddressプロパティが必須になります。 .WithRequiredPrincipal(ad => ad.Student)は、必要に応じてStudentAddressエンティティのStudentプロパティを作成します。したがって、必要な両端を構成します。そのため、住所のないStudentエンティティまたはStudentのないStudentAddressエンティティを保存しようとすると、例外がスローされます。