【问题标题】:Seed entity with owned property拥有财产的种子实体
【发布时间】:2018-06-14 16:58:14
【问题描述】:

我正在尝试在我的数据库中植入一个用户实体。 User 实体有一个 owend 属性 EmailPermissions

当我运行命令时

dotnet ef 迁移添加 Initial;

我得到了错误

无法添加实体类型“用户”的种子实体,因为它设置了导航“电子邮件权限”。要为关系播种,您需要将相关实体种子添加到“EmailPermissions”并指定外键值 {'UserId'}。

但由于 EmailPermissions 是一个拥有的实体,我没有给它显式的 UserId 属性,这意味着我不能在数据库中单独为其播种。

实体

public sealed class User : IdentityUser
{
    public User()
    {
        EmailPermissions = new EmailPermissions();
    }

    /* [..] */

    public string TemporaryEmailBeforeChange { get; set; }
    public bool IsEmailAwaitingUpdate { get; set; }
    public EmailPermissions EmailPermissions { get; set; }
    public ICollection<Registeration> Registerations { get; set; }

    /* [..] */

}

[Owned]
public class EmailPermissions
{
    /* [..] */

    public bool Newsletter { get; set; }
    public bool PromotionalOffers { get; set; }
    public bool PrestationReminders { get; set; }
    public bool PrestationOffers { get; set; }
}

播种电话

private void SeedUser(ModelBuilder builder)
{
    builder.Entity<User>().HasData(
        new User
        {
            Id = "37846734-172e-4149-8cec-6f43d1eb3f60",
            Email = "foo@foo.foo",
            UserName = "foo@foo.foo",
            PasswordHash = "AQAAAAEAACcQAAAAEIytBES+jqKH9jfuY3wzKyduDZruyHMGE6P+ODe1pSKM7BuGjd3AIe6RGRHrXidRsg==",
            SecurityStamp = "WR6VVAGISJYOZQ3W7LGB53EGNXCWB5MS",
            ConcurrencyStamp = "c470e139-5880-4002-8844-ed72ba7b4b80",
            EmailConfirmed = true
        });
}   

如果我从构造函数中删除 EmailPermissions 属性的实例化,则会收到以下错误

“User”类型的实体正在与“EmailPermissions”类型的实体共享表“AspNetUsers”,但没有此类型的实体具有已标记为“Added”的相同键值。

当用户拥有自有属性时,如何通过 .HasData 方法为用户播种?

【问题讨论】:

标签: c# entity-framework-core ef-core-2.1


【解决方案1】:

目前文档中缺少此信息(由#710: Document how to seed owned types 跟踪)。 EF Core 团队(举例)在#12004: Problem seeding data that contains owned type 线程中对此进行了解释:

拥有的类型必须在OwnsOne 调用之后使用HasData 调用播种。此外,由于按惯例拥有的类型具有在影子状态下生成的主键,并且由于种子数据需要定义键,因此这需要使用匿名类型并设置键。

这基本上就是异常消息告诉你的内容。

按照建议,您应该从构造函数中删除 EmailPermissions 属性的实例化,并添加如下种子代码:

builder.Entity<User>().OwnsOne(e => e.EmailPermissions).HasData(
    new
    {
        UserId = "37846734-172e-4149-8cec-6f43d1eb3f60",
        // other properties ...
    }
);

由于需要知道影子 PK 名称和匿名类型的使用,这很烦人且容易出错。正如同一个成员提到的

请注意,如果播种支持导航,这将变得更容易,这由 #10000: Data Seeding: Add support for navigations 跟踪

【讨论】:

  • 您的回答已经过去了 13 个月。我刚遇到同样的问题。是我做错了什么还是该功能仍未修复?
  • @Konrad 点开最后一个链接(#10000)可以看到,最后一个回复是“在积压中,所以大概2019年以后完成。 "
【解决方案2】:

感谢 Ivan Stoev 的回答。我添加了一些更容易想象的代码。 这是基于示例的种子数据函数代码。

  • 首先添加用户的数据。
  • 之后添加拥有对象的数据。
  • 拥有对象的数据必须是匿名的,因为 PK 会请求。此 PK 不会出现在数据库中。名称应为实体名称 + Id

示例:实体 XXX => PK 将是 XXXId

private void SeedUser(ModelBuilder builder)
{
    builder.Entity<User>(b =>
    {
        b.HasData(new User
        {
            Id = "37846734-172e-4149-8cec-6f43d1eb3f60",
            Email = "foo@foo.foo",
            UserName = "foo@foo.foo",
            // more properties of User
        });
        b.OwnsOne(e => e.EmailPermissions).HasData(new 
        {
                UserId = "37846734-172e-4149-8cec-6f43d1eb3f60",
                Newsletter = true,
                PromotionalOffers = true,
                PrestationReminders = true,
                PrestationOffers = true
        });
    });
}

【讨论】:

    【解决方案3】:

    如果您想避免使用匿名类型来指定影子属性键,您可以在模型类中显式声明它们,并使用 Fluent API 将它们配置为键。这样您就不必猜测属性名称,并且更不容易出错。

    如果提供给 Property 方法的名称与现有属性(影子属性或在实体类上定义的属性)的名称匹配,则代码将配置该现有属性,而不是引入新的影子属性。 Source

    【讨论】:

      【解决方案4】:

      在我的场景中,我希望在父类中自动初始化所​​拥有的类型属性:

      public class User
      {
        EmailPermissions _EmailPermissions;
        public EmailPermissions 
        {
          get => _EmailPermissions ??= new EmailPermissions();
          set => _EmailPermissions = value;
        }
      }
      

      当我尝试添加种子数据时,我遇到了那个讨厌的异常。

      解决方案是在其 HasData 调用中将 User 作为匿名类型传递。

      【讨论】:

        【解决方案5】:

        我在启动时播种了我的数据时遇到了同样的问题。 Here is the link to the github issue.

        【讨论】:

          猜你喜欢
          • 2013-12-09
          • 1970-01-01
          • 2019-05-08
          • 2023-04-07
          • 2012-11-11
          • 1970-01-01
          • 2011-11-29
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多