如何使用 Entity Framework 4 Code First (POCO) 声明一对一关系?
我发现了这个问题(Entity Framework 4中的一对一关系),但是答案引用的文章没有用(有一行代码是1-1关系,但没有提到如何定义它)。
如何使用 Entity Framework 4 Code First (POCO) 声明一对一关系?
我发现了这个问题(Entity Framework 4中的一对一关系),但是答案引用的文章没有用(有一行代码是1-1关系,但没有提到如何定义它)。
三种方法:
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; }
}
我没有使用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。是的,互联网上有很多使用它的代码示例。不,你仍然不应该使用它。
你只是在寻找这样的东西吗?
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")
如果您想包含“导航属性”,还记得打电话。
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 配置一对零或一关系:
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 属性。因此,它配置了所需的两端。所以现在,当您尝试保存没有地址的学生实体或没有学生的学生地址实体时,它会抛出异常。