12

如何使用 Entity Framework 4 Code First (POCO) 声明一对一关系?

我发现了这个问题(Entity Framework 4中的一对一关系),但是答案引用的文章没有用(有一行代码是1-1关系,但没有提到如何定义它)。

4

4 回答 4

22

三种方法:

A) 相互声明具有导航属性的两个类。在其主键上使用 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 张表:一张用于 AppUser,一张用于 AdminUser。AdminUser 与 AppUser 是 1:1 并且是 Dependent - 这意味着您可以删除 AdminUser,但是如果您在 AdminUser 仍然指向 AppUser 时删除它,您将收到约束违规错误。​</p>

C) EF中做一对一的中途方法有2种:

Entity-Splitting,你有一个类,但它存储在一个主表和 1 个或多个一对一相关的表中。

Table-Splitting,其中一棵对象树平展成一个表。例如,具有 Address 属性的类会将 Address 对象的列(如 Address_City)扁平化为单个表。

*如果您想延迟加载它们,您可以在任何 EF 属性或集合中包含 virtual 。如果将具有延迟加载属性的对象传递给例如 MVC JSON 转换器或遍历对象层次结构的任何其他对象,这可能会导致无限循环或加载整个数据库。延迟加载总是同步完成,阻塞线程,没有任何通知。总而言之,可以使用它冻结代码、应用程序或服务器的方法列表很长。避免在 EF 类上使用 virtual。是的,互联网上有很多使用它的代码示例。不,你仍然不应该使用它。

于 2012-05-31T16:57:25.843 回答
11

你只是在寻找这样的东西吗?

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

编辑:是的,我面前没有 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")如果您想包含“导航属性”,还记得打电话。

于 2010-09-01T22:43:55.557 回答
1
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,我认为这应该可以让您到达那里。

于 2010-09-15T02:51:42.897 回答
1

以以下 Student 和 StudentAddress 实体为例。
使用 DataAnnotations 配置一对零或一关系:

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 配置它。考虑以下具有属性名称 StudentId 而不是 StudentAddressId 的 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 配置一对零或一关系:
当 Student 和 StudentAddress 遵循约定时:Student 和 StudentAddress 实体遵循 PrimaryKey 的默认代码优先约定。因此,我们不需要配置它们来定义它们的 PrimaryKey。我们只需要配置 StudentAddress 实体,StudentAddressId 应该是 ForeignKey。

以下示例使用 Fluent API 在 Student 和 StudentAddress 之间设置一对零或一对关系。

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 实体时需要,在没有Student 导航属性的情况下保存StudentAddress 实体时会抛出异常)。这将使 StudentAddressId 也成为 ForeignKey。

因此,您可以在两个实体之间配置一对零或一的关系,其中可以在不附加 StudentAddress 对象的情况下保存 Student 实体,但如果不附加 Student 实体的对象,则无法保存 StudentAddress 实体。这使得需要一个末端。

当 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 配置一对一关系:
我们可以使用 Fluent API 配置实体之间的一对一关系,其中需要两端,即 Student 实体对象必须包含 StudentAddress 实体对象,StudentAddress 实体必须依次包含 Student 实体对象保存它。

注意:一对一的关系在 MS SQL Server 中技术上是不可能的。它总是一对零或一。EF 在不在数据库中的实体上形成一对一的关系。

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 属性。因此,它配置了所需的两端。所以现在,当您尝试保存没有地址的学生实体或没有学生的学生地址实体时,它会抛出异常。

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

于 2016-07-01T14:17:14.313 回答