web-dev-qa-db-ja.com

Seed所有プロパティを持つエンティティ

データベースにユーザーエンティティをシードしようとしています。 Userエンティティには、owendプロパティEmailPermissionsがあります。

コマンドを実行すると

dotnet ef migrationsは初期を追加します。

エラーが出る

エンティティタイプ「ユーザー」のシードエンティティには、ナビゲーション「EmailPermissions」が設定されているため、追加できません。関係をシードするには、関連するエンティティシードを '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 = "[email protected]",
            UserName = "[email protected]",
            PasswordHash = "AQAAAAEAACcQAAAAEIytBES+jqKH9jfuY3wzKyduDZruyHMGE6P+ODe1pSKM7BuGjd3AIe6RGRHrXidRsg==",
            SecurityStamp = "WR6VVAGISJYOZQ3W7LGB53EGNXCWB5MS",
            ConcurrencyStamp = "c470e139-5880-4002-8844-ed72ba7b4b80",
            EmailConfirmed = true
        });
}   

コンストラクタからEmailPermissionsプロパティのインスタンス化を削除すると、代わりに次のエラーが発生します

タイプ「User」のエンティティは、テーブル「AspNetUsers」をタイプ「EmailPermissions」のエンティティと共有していますが、「Added」としてマークされている同じキー値を持つこのタイプのエンティティはありません。

所有するプロパティがある場合、.HasDataメソッドを介してユーザーをシードするにはどうすればよいですか?

16
Mathieu VIALES

現在、この情報はドキュメントから欠落しています( #710:所有するタイプをシードする方法のドキュメント によって追跡されます)。 #12004:所有タイプを含むデータのシードの問題 スレッドでEF Coreチーム(例を含む)によって説明されています:

所有する型は、HasData呼び出しの後にOwnsOne呼び出しでシードする必要があります。また、慣例により所有されるタイプにはシャドー状態で生成される主キーがあり、シードデータにはキーを定義する必要があるため、匿名タイプの使用とキーの設定が必要です。

これは基本的に、例外メッセージが伝えていることです。

アドバイスに従って、EmailPermissionsプロパティのインスタンス化をコンストラクターから削除し、次のようなシードコードを追加する必要があります。

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

シャドウPK名と匿名タイプの使用法を知る必要があるため、かなり煩わしく、エラーが発生しやすくなります。同じメンバーが言及したように

#10000:データシード:ナビゲーションのサポートを追加する によって追跡されるシードがナビゲーションでサポートされている場合、これは簡単になることに注意してください。

22
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 = "[email protected]",
            UserName = "[email protected]",
            // 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
        });
    });
}
2
Long Pham

匿名型を使用してシャドウプロパティキーを指定することを避けたい場合は、モデルクラスでそれらを明示的に宣言し、Fluent APIをキーとして設定できます。これにより、プロパティ名を推測する必要がなくなり、エラーが発生しにくくなります。

Propertyメソッドに指定された名前が既存のプロパティ(シャドウプロパティまたはエンティティクラスで定義されたプロパティ)の名前と一致する場合、コードは新しいシャドウプロパティを導入するのではなく、既存のプロパティを構成します。 ソース

1
Shadow

起動時に同じ問題でデータがシードされました。 これはgithubの問題へのリンクです

0
Mohsen

私のシナリオでは、所有クラスのプロパティを親クラスで自動初期化する必要がありました。

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

シードデータを追加しようとすると、厄介な例外が発生しました。

解決策は、User呼び出しでHasDataを匿名型として渡すことでした。

0
Shimmy