データベースにユーザーエンティティをシードしようとしています。 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
メソッドを介してユーザーをシードするにはどうすればよいですか?
現在、この情報はドキュメントから欠落しています( #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:データシード:ナビゲーションのサポートを追加する によって追跡されるシードがナビゲーションでサポートされている場合、これは簡単になることに注意してください。
イヴァン・ストエフの回答に感謝します。想像しやすいようにコードをいくつか追加します。これは、例に基づいたシードデータ関数のコードです。
例:エンティティ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
});
});
}
匿名型を使用してシャドウプロパティキーを指定することを避けたい場合は、モデルクラスでそれらを明示的に宣言し、Fluent APIをキーとして設定できます。これにより、プロパティ名を推測する必要がなくなり、エラーが発生しにくくなります。
Propertyメソッドに指定された名前が既存のプロパティ(シャドウプロパティまたはエンティティクラスで定義されたプロパティ)の名前と一致する場合、コードは新しいシャドウプロパティを導入するのではなく、既存のプロパティを構成します。 ソース
起動時に同じ問題でデータがシードされました。 これはgithubの問題へのリンクです
私のシナリオでは、所有クラスのプロパティを親クラスで自動初期化する必要がありました。
public class User
{
EmailPermissions _EmailPermissions;
public EmailPermissions
{
get => _EmailPermissions ??= new EmailPermissions();
set => _EmailPermissions = value;
}
}
シードデータを追加しようとすると、厄介な例外が発生しました。
解決策は、User
呼び出しでHasData
を匿名型として渡すことでした。