web-dev-qa-db-ja.com

Entity Framework 4 Code First(POCO)を使用して1対1の関係を宣言する方法

Entity Framework 4 Code First(POCO)を使用して1対1の関係を宣言するにはどうすればよいですか?

この質問(Entity Framework 4の1対1の関係) が見つかりましたが、回答が参照している記事は役に立ちませんでした(1-1の関係であるコードが1行ありますが、それを定義する方法についての言及はありません)。

12
Paul Hiles

あなたはこのようなものを探していますか?

_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")を呼び出してください。

11
TheCloudlessSky

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

http://weblogs.asp.net/manavi/archive/2011/05/01/associations-in-ef-4-1-code-first-part-5-one-to-one-foreign- key-associations.aspx

私は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クラスで仮想を使用することは避けてください。はい、インターネット上にはそれを使用するコードサンプルがたくさんあります。いいえ、まだ使用しないでください。

22
Chris Moschini
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を追加すると、そこに到達するはずです。

1
Terrence

次の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エンティティを保存しようとすると、例外がスローされます。

参照: http://www.entityframeworktutorial.net/code-first/configure-one-to-one-relationship-in-code-first.aspx

1
Skull