拥有 属性 的种子实体

Seed entity with owned property

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

当我运行命令

dotnet ef migrations add Initial;

我收到错误

The seed entity for entity type 'User' cannot be added because it has the navigation 'EmailPermissions' set. To seed relationships you need to add the related entity seed to 'EmailPermissions' and specify the foreign key values {'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 属性 的实例化,则会出现以下错误

The entity of type 'User' is sharing the table 'AspNetUsers' with entities of type 'EmailPermissions', but there is no entity of this type with the same key value that has been marked as 'Added'.

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

目前文档中缺少此信息(由 #710: Document how to seed owned types). It's explained by EF Core team (with example) in the #12004: Problem seeding data that contains owned type 线程跟踪:

Owned types must be seeded with a HasData call after the OwnsOne call. Also, since owned types by convention have a primary key generated in shadow state, and since seed data requires keys to be defined, then this requires use of an anonymous type and setting the key.

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

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

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

由于需要知道影子 PK 名称和匿名类型的用法,因此非常烦人且容易出错。正如同一位成员提到的

Note that this would become easier if navigations were supported for seeding, which is tracked by #10000: Data Seeding: Add support for navigations

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

If the name supplied to the Property method matches the name of an existing property (a shadow property or one defined on the entity class), then the code will configure that existing property rather than introducing a new shadow property. Source

感谢 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
        });
    });
}

在我的场景中,我希望拥有的类型 属性 在父 class:

中自动初始化
public class User
{
  EmailPermissions _EmailPermissions;
  public EmailPermissions 
  {
    get => _EmailPermissions ??= new EmailPermissions();
    set => _EmailPermissions = value;
  }
}

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

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

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