【问题标题】:Owned types problem in Entity Framework Core 3.0Entity Framework Core 3.0 中的拥有类型问题
【发布时间】:2019-10-09 04:13:07
【问题描述】:

我有 EF Core 3.0 preview4 的奇怪行为。 我有主课:

public enum ClientType
{
    Customer = 0,
    Produces = 1,
}

public class User : IdentityUser<Guid>
{
    public ClientType ClientType { get; set; }
    public SMSCodeInfo SMSCodeInfo { get; set; }
    public string Discriminator { get; set; }
}

在这段代码中 SMSCodeInfo 是一个类:

public class SMSCodeInfo
{ 
    public long Code { get; set; }
    public DateTime Expiration { get; set; }

    public SMSCodeInfo() { }
    public SMSCodeInfo(int days, int hours, int minutes) : this(DateTime.Now.AddDays(days).AddHours(hours).AddMinutes(minutes)) { }
    public SMSCodeInfo(DateTime Expiration)
    {
        this.Expiration = Expiration;
        Code = new Random().Next(100000, 999999);
    }        
}

我尝试通过不同的方法将SMSCodeInfo作为拥有数据添加到User类中:[Owned]属性用法,在OnModelCreating(ModelBuilder modelBuilder)方法中添加代码:

modelBuilder.Entity<User>().OwnsOne(o => o.SMSCodeInfo);

但是每次我进行迁移时,我都会得到 2 个表:AspNetUsers 带有关于用户的身份信息(我在我的项目中使用 Asp.net 核心身份)和AspNetUsers1 表,其中包含 SMSCodeInfo 成员. EF Core 生成此代码用于迁移,我无法理解“为什么”!

        migrationBuilder.CreateTable(
            name: "AspNetUsers1",
            columns: table => new
            {
                UserId = table.Column<Guid>(nullable: false),
                Code = table.Column<long>(nullable: false),
                Expiration = table.Column<DateTime>(nullable: false)
            },
            constraints: table =>
            {
                table.PrimaryKey("PK_AspNetUsers1", x => x.UserId);
                table.ForeignKey(
                    name: "FK_AspNetUsers1_AspNetUsers_UserId",
                    column: x => x.UserId,
                    principalTable: "AspNetUsers",
                    principalColumn: "Id",
                    onDelete: ReferentialAction.Cascade);
            });

“附加”。我希望将拥有的数据添加到 AspNetUser 表中,而不是单独的表中。

附:从 IdentityUser 派生的类会出现此问题。对于我的项目代码中的其他类,像 modelBuilder.Entity&lt;TOwner&gt;().OwnsOne(o =&gt; o.ToBeOwned); 这样的工作正确并提供具有注入的拥有类型的正确表。

【问题讨论】:

    标签: c# entity-framework asp.net-core .net-core


    【解决方案1】:

    根据Owned Entity Types in EF CoremodelBuilder.Entity&lt;User&gt;().OwnsOne(o =&gt; o.SMSCodeInfo); 应该在Owner 表中创建[Owned] 实体列,但奇怪的是它创建了一个具有奇怪名称AspNetUsers1 的单独表。

    如果您希望[Owned] 实体列位于单独的表中,那么您的配置应如下所示:

    modelBuilder.Entity<User>().OwnsOne(o => o.SMSCodeInfo , sm => 
    {
       sm.ToTable("SMSCodeInfo");
    });
    

    会生成如下:

    migrationBuilder.CreateTable(
                name: "SMSCodeInfo",
                columns: table => new
                {
                    UserId = table.Column<Guid>(nullable: false),
                    Code = table.Column<long>(nullable: false),
                    Expiration = table.Column<DateTime>(nullable: false)
                },
                constraints: table =>
                {
                    table.PrimaryKey("PK_SMSCodeInfo", x => x.UserId);
                    table.ForeignKey(
                        name: "FK_SMSCodeInfo_AspNetUsers_UserId",
                        column: x => x.UserId,
                        principalTable: "AspNetUsers",
                        principalColumn: "Id",
                        onDelete: ReferentialAction.Cascade);
                });
    

    【讨论】:

    • 我希望将其添加到 AspNetUsers 表中,而不是单独的表中。
    • @Dmitriy 然后modelBuilder.Entity&lt;User&gt;().OwnsOne(o =&gt; o.SMSCodeInfo); 应该已经足够了,正如我在回答中所说的那样。你会检查是否有其他配置在这里干预?
    • 所以,这是一个错误?不行为定义?
    • 这是我想要的解决方案,但改动很小:sm.ToTable("SMSCodeInfo"); 应更改为 sm.ToTable("AspNetUsers");。谢谢!!
    • 很高兴看到您提出了解决方案。虽然它应该在没有sm.ToTable("AspNetUsers"); 的情况下自动完成。顺便说一句,祝你好运。
    【解决方案2】:

    也许对其他人也有帮助。由于使用 efcore v2.2.6 时出现相同的症状,因此刚刚登陆此页面。主题上的microsoft documentation 建议在类上添加 [Owned] 属性应该足以让“表共享/拆分”功能开箱即用。

    我的设置:

    [Owned]
    public class Audit {
        public string By {get; set;}
        public DateTime At {get;set;}
    }
    
    public class Organisation {
        public Guid Id {get;set;}
        public Audit Created {get;set;}
        // other properties omitted 
    }
    

    我的数据库架构

    CREATE TABLE Organisations (
       Id char(36),   -- I'm using MySQL8
       Created_At DateTime NOT NULL,
       Created_By varchar(32) NOT NULL,
       PRIMARY KEY Id
    )
    

    运行我的示例时,我收到以下消息:

    InvalidOperationException:实体类型“审计”需要定义主键。

    这似乎表明 efcore 正在尝试在单独的表中查找审核数据。我最终做的是在@Dmitry 提到的 DbContext 类中添加额外的配置,诀窍是指定表:

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            base.OnModelCreating(modelBuilder);
    
            modelBuilder.Entity<Organisation>().OwnsOne(org => org.Created, created => created.ToTable("Organisation"));
        }
    

    我不确定这是否是一个错误,或者预计会这样工作。但我发现文档留下了解释的空间。我创建了以下扩展方法来消除每个实体类型的手动配置:

        public static ModelBuilder FixOwnedAttributeImplementation(this ModelBuilder modelBuilder)
        {
            // loop over all entities
            foreach (var entity in modelBuilder.Model.GetEntityTypes())
            {
                // find properties that reference a type that is marked as [Owned]
                foreach (var property in entity.GetNavigations().Where(p => p.ClrType.GetCustomAttributes(typeof(OwnedAttribute), true).Any()))
                {
                    // enforce table sharing
                    modelBuilder.Entity(entity.Name).OwnsOne(property.ClrType, property.AsNavigation().Name).ToTable(entity.Relational().TableName);
                }
            }
    
            return modelBuilder;
        }
    

    所以你只需要添加:

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);
    
        modelBuilder.FixOwnedAttributeImplementation();
    }
    

    【讨论】:

    • 随着对 efcore 3.1 的更新,扩展方法失败,但未定义 AsNavigation() 除外。作为一种解决方法,您可以直接使用 property.Name。
    猜你喜欢
    • 1970-01-01
    • 2021-10-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-04-13
    • 2020-03-19
    相关资源
    最近更新 更多