私はVS2012でMVC4を使用しており、タイプごとのテーブル継承アプローチに従っています。Seed()
メソッドを使用してデータベースにデータを追加しようとしています。
私は次のクラスを持っています:
家主
[Table("Landlord")]
public class Landlord : UserProfile
{
// A Landlord can have many ResidentialProperties
[ForeignKey("ResidentialPropertyId")]
public virtual ICollection<ResidentialProperty> ResidentialProperties { get; set; }
}
住宅物件
[Table("ResidentialProperty")]
public class ResidentialProperty
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int ResidentialPropertyId { get; set; }
// A ResidentialProperty has 1 Landlord
public virtual int UserId { get; set; }
public virtual UserProfile UserProfile { get; set; }
}
からLandlord
継承するUserProfile
ため、 に PK プロパティはありませんLandlord.
私が実装しようとしている関係は次のとおりです。
- A
Landlord
は複数持つことができますResidentialProperties
(1:n) ResidentialProperty
(アプリケーションのスコープ内で) 1 つLandlord
(1:1)を持つことができます
update-database
モデルを構成した方法で、パッケージマネージャーからコマンドを正常に実行し、Seed()
メソッドにあるものをデータベースに追加するには十分だと思いました。これは当てはまりませんでした。次のエラーが発生しました。
「LetLord.Models.ResidentialProperty_UserProfile」関係のプリンシパル エンドを特定できません。追加された複数のエンティティが同じ主キーを持つ場合があります。
いろいろ読んだ後、作成したい関係は Fluent API を使用して実装する必要があることに気付き、次のことを試しました。
modelBuilder.Entity<Landlord>()
.HasMany(x => x.ResidentialProperties)
.WithOptional()
.HasForeignKey(x => x.ResidentialPropertyId);
modelBuilder.Entity<ResidentialProperty>()
.HasRequired(x => x.UserProfile);
パッケージ マネージャー経由でコマンドを実行しようとするとupdate-database
、次のエラーが発生します。
\tSystem.Data.Entity.Edm.EdmAssociationEnd: : 関係 'Landlord_ResidentialProperties' のロール 'Landlord_ResidentialProperties_Target' で多重度が無効です。依存ロールはキー プロパティを参照するため、依存ロールの多重度の上限は '1' でなければなりません。
私は自分の問題を解決する方法について完全に途方に暮れています。私はこれに非常に多くの時間を費やしてきましたが、これは簡単に達成できるはずだと感じているため、非常にイライラしています。誰かが解決策を持っている場合は、私と共有していただければ幸いです。
編集- 上記の最初のエラーからスローされた例外の出力:
System.Data.Entity.Infrastructure.DbUpdateException: 'LetLord.Models.ResidentialProperty_Landlord' 関係のプリンシパル エンドを特定できません。追加された複数のエンティティが同じ主キーを持つ場合があります。---> System.Data.UpdateException: 'LetLord.Models.ResidentialProperty_Landlord' 関係のプリンシパル エンドを特定できません。追加された複数のエンティティが同じ主キーを持つ場合があります。System.Data.Mapping.Update.Internal.UpdateTranslator.RegisterEntityReferentialConstraints (IEntityStateEntry stateEntry、ブール currentValues) で System.Data.Mapping.Update.Internal.UpdateTranslator.RegisterReferentialConstraints (IEntityStateEntry stateEntry) で System.Data.Mapping.Update.Internal. System.Data.Mapping.Update.Internal.UpdateTranslator の UpdateTranslator.PullModifiedEntriesFromStateManager()。
編集- 解決策:
エンティティは、以下の Ladislav の回答に従っている必要があります。私の間違いは、派生クラスではなく、基本クラスの多くの側でナビゲーション プロパティを作成していたことです。
私の次のエラーは、Seed()
メソッド内のエンティティの順序付けでした。以下に、それらがどのようなものであるべきかを追加しました。これは、関連付けのあるエンティティを追加する場合に特に当てはまります。
protected override void Seed(LetLord.Models.LetLordContext context)
{
try
{
#region Landlords
// create a list of landlords first - a landlord can have many residential properties, make this = new List<ResidentialProperty>()
var landlords = new List<Landlord>
{
// landlord 1
new Landlord { UserName="Frank", FirstName="Frank", LastName="O'Sullivan", Email="a@a.com", AccountType=(int)AccountType.Landlord, ResidentialProperties = new List<ResidentialProperty>() }
// ...
};
landlords.ForEach(l => context.UserProfile.AddOrUpdate(l));
context.SaveChanges();
#endregion
#region ResidentialProperties
// then create a list of properties
var residentialProperties = new List<ResidentialProperty>
{
// Property 1 associated with LandLord 1
new ResidentialProperty { PropTypeValue=(int)PropertyType.Detached, Description="Some description...", NumberOfBedrooms="4", NumberOfReceptionRooms="2", NumberOfBathrooms="2", HasBackGarden=true, HasFrontGarden=true, HasSecureParking=false, IsDisabledFriendly=false, DateAdded=DateTime.Now, Landlord = landlords.FirstOrDefault(u => u.UserId == 11) } // in my case, user 11 is landlord 1
// ...
};
residentialProperties.ForEach(rp => context.ResidentialProperty.AddOrUpdate(rp));
context.SaveChanges();
#endregion
// then add our list of properties to our list of landlords
landlords[0].ResidentialProperties.Add(residentialProperties[0]);
landlords[1].ResidentialProperties.Add(residentialProperties[1]);
landlords[2].ResidentialProperties.Add(residentialProperties[2]);
landlords[3].ResidentialProperties.Add(residentialProperties[3]);
landlords[4].ResidentialProperties.Add(residentialProperties[4]);
landlords[5].ResidentialProperties.Add(residentialProperties[5]);
landlords[6].ResidentialProperties.Add(residentialProperties[6]);
landlords[7].ResidentialProperties.Add(residentialProperties[7]);
landlords[8].ResidentialProperties.Add(residentialProperties[8]);
landlords[9].ResidentialProperties.Add(residentialProperties[9]);
context.SaveChanges();
}
catch (Exception ex)
{
string lines = ex.ToString();
System.IO.StreamWriter file = new System.IO.StreamWriter(@"C:\Users\Home\Desktop\error.txt");
file.WriteLine(lines);
file.Close();
}
update-database
上記のようにコードを修正して実行しようとすると、次のエラーが発生しました。
System.Data.Entity.Infrastructure.DbUpdateException: エントリの更新中にエラーが発生しました。詳細については、内部例外を参照してください。---> System.Data.UpdateException: エントリの更新中にエラーが発生しました。詳細については、内部例外を参照してください。---> System.Data.SqlClient.SqlException: INSERT ステートメントが FOREIGN KEY 制約 "FK_dbo.ResidentialProperty_dbo.Landlord_UserId" と競合しました。データベース「C:\USERS\HOME\DESKTOP\LETLORD\LETLORD\APP_DATA\LETLORD.MDF」、テーブル「dbo.Landlord」、列「UserId」で競合が発生しました。ステートメントは終了されました。
簡単な検索の後、データベースに既にデータがあったため、このエラーが発生していることがわかりました(と思います)。データベースを削除して再作成することでこれを回避し、Seed()
メソッドを再度実行すると、データが正常に追加されました。