实体框架核心一对多关系

时间:2016-01-27 03:22:49

标签: c# one-to-many entity-framework-core

我正在尝试构建具有一对多关系的代码第一个数据模型。

UserDTO和RoleDTO模型的用户包含一对多角色。

RoleDTO的一些简化代码是:

internal class RoleDTO
{
    public UserDTO User { get; set; }

    public string Value { get; set; }

    public RoleDTO()
    {

    }
}

简化的UserDTO:

internal class UserDTO
{
    public string Email { get; set; }

    public string FullName { get; set; }

    public string UserName { get; set; }

    public virtual ICollection<RoleDTO> Roles { get; set; }

    public UserDTO()
    {

    }
}

最后是继承自DBContext的类

class StorageContext : DbContext
{
    public DbSet<UserDTO> Users { get; set; }

    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        optionsBuilder.UseSqlServer(@"My_Connection_String");
    }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<UserDTO>().HasKey(entity => entity.Email);
        modelBuilder.Entity<UserDTO>().HasMany(entity => entity.Roles);

        modelBuilder.Entity<RoleDTO>().HasOne<UserDTO>().WithMany(x => x.Roles);
        modelBuilder.Entity<RoleDTO>().HasKey(x => x.Value);
    }
}

当我运行添加迁移并且我没有数据库或迁移文件夹时(即使我在删除了一些列之后运行它),还会创建一个带有额外列的Role表来引用用户的电子邮件地址。

我只想在我的角色表中有两列,一个是用户的电子邮件地址,是用户电子邮件PK的FK,另一个是与外键组合的字符串值用户表中的复合主键。下图:

*******************************
* Role                        *
*******************************
* User_Email - string/varchar *
* Value - string /varchar     *
*******************************
FK - User_Email to User table Email column
PK - User_Email, Value compound primary key

有没有人知道如何使用流畅的API或数据注释来实现这一目标?

谢谢!

2 个答案:

答案 0 :(得分:1)

我不确定你要用它来实现什么,但在ORM中使用复合键可能很复杂,而且这个区域可能无法预测。所以我建议不要使用复合键,除非你有充分的理由。

我建议不要在任何地方使用电子邮件地址作为PK。使用唯一生成的Int32 / Int64甚至Guid作为主键(一旦您设置了关系,这将在您的Roles表中反映为外键)并在您的电子邮件列(在您的用户表中)中放置一个索引对它的独特约束。您可以在迁移脚本中添加索引。至少通过这种方式,如果要更改电子邮件地址,您可以保持身份(如果这是您想要的)。

我希望这会有所帮助。

答案 1 :(得分:1)

首先,类UserDTO没有名为User_Email的属性。您可以使用EF在模型中定义阴影属性,但考虑到您希望它在模型中,您应该在类public string User_Email上定义属性

在类上定义属性或使用fluent API在模型中定义的shadow属性

要定义复合主键,您需要使用HasKey流畅的API。您无法使用数据注释来定义复合主键。

要将User_Email, Value定义为复合PK,请在OnModelCreating中编写以下代码(如果按照这种方式定义阴影属性后)

modelBuilder.Entity<UserDTO>().HasKey(e => new { e.User_Email, e.Value });

要为关系指定外键属性,您可以在fk属性/导航上使用ForeignKeyAttribute,也可以使用HasForeignKey流畅的API。

要将User_Email设置为FK到UserDTO表,假设UserDTO User中有RoleDTO&amp; ICollection<RoleDTO> Roles中的UserDTO是此关系的导航。

使用数据注释, 在ForeignKey("User")属性上写User_Email或在导航(ForeignKey("User_Email") / User)上写Roles。请记住,如果您将User_Email作为影子属性,则无法使用此功能。

使用Fluent API, 在OnModelCreating中编写以下代码以完全配置关系

modelBuilder.Entity<RoleDTO>().HasOne(e => e.User).WithMany(e => e.Roles).HasForeignKey(e => e.User_Email);

以上将使用User&amp;创建一对多关系Roles导航使用User_Email作为外键属性。

您的代码无法正常工作的原因是

  1. 使用HasKey,您已经定义了单个属性PK而不是复合PK。
  2. 您尚未使用HasForeignKey方法指定外键属性,因此EF将为您添加阴影属性。此外,在您的关系配置中,您仅使用Roles导航。 EF将尝试使用User导航创建另一种关系,有效地在UserDTORoleDTO之间建立2种关系。 =UNIQUE(query(Suoritusanalyysi!$D2:$K, ʺSELECT D,Max(F),Max(G),Max(H),Max(I),Max(J),Max(K),Avg(F),Avg(G),Avg(H),Avg(I),Avg(K),Sum(F),Sum(K),Count(F) Where D !='' Group By D Label Max(F) 'Max vol.', Max(G) 'Max tih.', Max(H) 'Max int.', Max(I) 'Max sarja', Max(J) 'Max km. int.', Max(K) 'Max toistot', Avg(F) 'Km. vol.', Avg(G) 'Km. tih.', Avg(H) 'Km. int.', Avg(I) 'Km. sarja', Avg(K) 'Km. toistot', Sum(F) 'Total vol.', Sum(K) 'Total toistot', Count(F) 'Kerrat'ʺ))。这就是你获得额外列的原因,因为EF根据惯例创建了阴影属性以用作外键属性。