Entity Framework Core OwnsOne 创建单独的表,而不是像预期的那样将属性添加到同一个表中

Posted

技术标签:

【中文标题】Entity Framework Core OwnsOne 创建单独的表,而不是像预期的那样将属性添加到同一个表中【英文标题】:Entity Framework Core OwnsOne Creates Separate Tables, Rather than adding properties to the Same Table as Expected 【发布时间】:2019-02-10 21:07:01 【问题描述】:

我认为 Entity Framework Core 拥有的类型默认添加到与其所有者相同的表中。但我在迁移中没有看到这一点。

有人能告诉我吗?

有没有办法通过将 Name 属性直接添加到 Person 表来获得所需的迁移?

public class Person

    public Name Name  get; set; 


public class Name

    public string FirstName  get; set; 
    public string LastName  get; set; 


public class PersonConfiguration : IEntityTypeConfiguration<Person>

    public void Configure(EntityTypeBuilder<Person> person)
    
          person.OwnsOne(p => p.Name);
    

dotnet ef migrations add DidNotSeeThatComing 结果

public partial class DidNotSeeThatComing : Migration

    protected override void Up(MigrationBuilder migrationBuilder)
    
        migrationBuilder.CreateTable(
            name: "Name",
            columns: table => new
            
                FirstName = table.Column<string>(type: "varchar", nullable: true),
                LastName = table.Column<string>(type: "varchar", nullable: true),
                PersonId = table.Column<Guid>(nullable: false)
            ,
            constraints: table =>
            
                table.PrimaryKey("PK_Name", x => x.PersonId);
                table.ForeignKey(
                    name: "FK_Name_Person_PersonId",
                    column: x => x.PersonId,
                    principalTable: "Person",
                    principalColumn: "Id",
                    onDelete: ReferentialAction.Cascade);
            );
        );
    

【问题讨论】:

您的想法是正确的 - 默认情况下它与所有者位于同一个表中,我无法重现该问题。你有干净的复制项目吗? @IvanStoev 已验证使用干净的 repo 项目按预期工作 发生这种情况的项目中是否有任何异常代码?喜欢模型实体/反射上的循环并调用ToTable? Ugg 我用 foreach 对自己做了这个(modelBuilder.Model.GetEntityTypes() 中的var entity) entity.Relational().TableName = entity.Name; 嘿嘿,所以最后是不寻常的模型实体循环 :) 很高兴您解决了这个问题。 【参考方案1】:

我自己在不知不觉中用这个配置代码创建了这个

protected override void OnModelCreating(ModelBuilder modelBuilder)

   base.OnModelCreating(modelBuilder);
   foreach (var entity in modelBuilder.Model.GetEntityTypes())
   
       entity.Relational().TableName = entity.Name;
   

这是我正在使用的解决方法

[Owned] // Microsoft.EntityFrameworkCore
public class Name  ... 

protected override void OnModelCreating(ModelBuilder modelBuilder)

   base.OnModelCreating(modelBuilder);
   foreach (var entity in modelBuilder.Model.GetEntityTypes())
   
       if(!entity.ClrType.GetCustomAttributes().OfType<OwnedAttribute>().Any())
       
            entity.Relational().TableName = entity.Name;
       
   
    

【讨论】:

[Owned] 正是我所追求的。【参考方案2】:

尝试以下方法:

public class Person

    public Guid Id  get; set; 
    public Name Name  get; set; 


public class Name

    public Guid Id  get; set; 
    public Guid PersonId get;set;
    public Person Person  get; set; 
    public string FirstName  get; set; 
    public string LastName  get; set; 


public class PersonConfiguration : IEntityTypeConfiguration<Person>

    public void Configure(EntityTypeBuilder<Person> person)
    
        person.OwnsOne(p => p.Name,
            nCls =>
            
                nCls.HasOne(n => n.Person);
                nCls.HasKey(n => new n.Id, n.PersonId);
                nCls.HasForeignKey(m => m.PersonId);
                nCls.ToTable(nCls.OwnedEntityType.ClrType.Name);
            
        );
    

这应该会让你得到类似的东西:

protected override void Up(MigrationBuilder migrationBuilder)

// the table names are lower case because my convention is to generate all names in snake_case

    migrationBuilder.CreateTable(
        name: "persons",
        columns: table => new
        
            id = table.Column<Guid>(nullable: false)
        ,
        constraints: table =>
        
            table.PrimaryKey("pk_persons", x => x.id);
        );

    migrationBuilder.CreateTable(
        name: "name",
        columns: table => new
        
            id = table.Column<Guid>(nullable: false),
            person_id = table.Column<Guid>(nullable: false),
            first_name = table.Column<string>(maxLength: 256, nullable: true),
            last_name = table.Column<string>(maxLength: 256, nullable: true)
        ,
        constraints: table =>
        
            table.PrimaryKey("pk_name", x => new  x.id, x.person_id );
            table.ForeignKey(
                name: "fk_name_persons_person_id",
                column: x => x.person_id,
                principalTable: "persons",
                principalColumn: "id",
                onDelete: ReferentialAction.Cascade);
        );

【讨论】:

【参考方案3】:

@Davious 我今天遇到了完全相同的问题,感谢您分享您的解决方案。这是我的解决方案,您不需要OwnedAttribute,因为您也可以使用entity.IsOwned()。我总是尝试通过 DbContext 进行所有配置。

///[Owned] // Not needed anymore
public class Name  ... 

protected override void OnModelCreating(ModelBuilder modelBuilder)

   base.OnModelCreating(modelBuilder);
   modelbuilder.Entity<Person>().OwnsOne(p => p.Name)

   foreach (var entity in modelBuilder.Model.GetEntityTypes())
   
       if(!entity.IsOwned())
       
            entity.Relational().TableName = entity.Name;
       
   
  

【讨论】:

以上是关于Entity Framework Core OwnsOne 创建单独的表,而不是像预期的那样将属性添加到同一个表中的主要内容,如果未能解决你的问题,请参考以下文章

Entity Framework Core 快速开始

EF6 System.Data.Entity.Core.EntityKey 在 Entity Framework Core 中的等价物是啥?

张高兴的 Entity Framework Core 即学即用:创建第一个 EF Core 应用

Entity Framework Core 迁移命令

Entity Framework Core - 计算字段排序

Entity Framework Core 性能优化