エンティティフレームワークの移行を使用しています(自動移行モード)。すべて大丈夫ですが、1つ質問があります。
多対多の関係がある場合、どのようにデータをシードする必要がありますか?
たとえば、2つのモデルクラスがあります。
_public class Parcel
{
public int Id { get; set; }
public string Description { get; set; }
public double Weight { get; set; }
public virtual ICollection<BuyingItem> Items { get; set; }
}
public class BuyingItem
{
public int Id { get; set; }
public decimal Price { get; set; }
public virtual ICollection<Parcel> Parcels { get; set; }
}
_
単純なデータ(PaymentSystemクラスの場合)と1対多の関係をシードする方法を理解していますが、Seed
とParcel
のインスタンスを生成するには、BuyingItem
メソッドにどのコードを記述する必要がありますか? _Update-Database
_を実行するたびにデータを複製したくないので、DbContext.AddOrUpdate()
を使用することを意味します。
_protected override void Seed(ParcelDbContext context)
{
context.AddOrUpdate(ps => ps.Id,
new PaymentSystem { Id = 1, Name = "Visa" },
new PaymentSystem { Id = 2, Name = "Paypal" },
new PaymentSystem { Id = 3, Name = "Cash" });
}
_
_protected override void Seed(Context context)
{
base.Seed(context);
// This will create Parcel, BuyingItems and relations only once
context.AddOrUpdate(new Parcel()
{
Id = 1,
Description = "Test",
Items = new List<BuyingItem>
{
new BuyingItem() { Id = 1, Price = 10M },
new BuyingItem() { Id = 2, Price = 20M }
}
});
context.SaveChanges();
}
_
このコードはParcel
、BuyingItems
とそれらの関係を作成しますが、別のBuyingItem
で同じParcel
が必要な場合(多対多の関係があります)、2番目の区画でこのコードを繰り返します-データベース内でBuyingItems
を複製します(ただし)同じId
sを設定しました)。
例:
_protected override void Seed(Context context)
{
base.Seed(context);
context.AddOrUpdate(new Parcel()
{
Id = 1,
Description = "Test",
Items = new List<BuyingItem>
{
new BuyingItem() { Id = 1, Price = 10M },
new BuyingItem() { Id = 2, Price = 20M }
}
});
context.AddOrUpdate(new Parcel()
{
Id = 2,
Description = "Test2",
Items = new List<BuyingItem>
{
new BuyingItem() { Id = 1, Price = 10M },
new BuyingItem() { Id = 2, Price = 20M }
}
});
context.SaveChanges();
}
_
同じBuyingItem
を異なるParcel
sに追加するにはどうすればよいですか?
EFコードで多対多の関係を構築するのと同じ方法で、多対多の関係を埋める必要があります。
protected override void Seed(Context context)
{
base.Seed(context);
// This will create Parcel, BuyingItems and relations only once
context.AddOrUpdate(new Parcel()
{
Id = 1,
Description = "Test",
Items = new List<BuyingItem>
{
new BuyingItem() { Id = 1, Price = 10M },
new BuyingItem() { Id = 2, Price = 20M }
}
});
context.SaveChanges();
}
データベースで使用されるId
を指定することは非常に重要です。それ以外の場合は、各Update-Database
は新しいレコードを作成します。
AddOrUpdate
はリレーションの変更をサポートしていないため、次の移行でリレーションを追加または削除するために使用することはできません。必要に応じて、Parcel
にBuyingItems
をロードし、ナビゲーションコレクションでRemove
またはAdd
を呼び出して新しいリレーションを解除または追加することにより、リレーションを手動で削除する必要があります。
完全な回答については、以下の「AddOrUpdateを適切に使用する」セクションを必ずお読みください。
まず、重複を排除するために、複合主キー(区画IDとアイテムIDで構成される)を作成しましょう。 DbContextクラスに次のメソッドを追加します。
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<Parcel>()
.HasMany(p => p.Items)
.WithMany(r => r.Parcels)
.Map(m =>
{
m.ToTable("ParcelItems");
m.MapLeftKey("ParcelId");
m.MapRightKey("BuyingItemId");
});
}
次に、Seedメソッドを次のように実装します:
protected override void Seed(Context context)
{
context.Parcels.AddOrUpdate(
p => p.Id,
new Parcel { Id = 1, Description = "Parcel 1", Weight = 1.0 },
new Parcel { Id = 2, Description = "Parcel 2", Weight = 2.0 },
new Parcel { Id = 3, Description = "Parcel 3", Weight = 3.0 }
);
context.BuyingItems.AddOrUpdate(
b => b.Id,
new BuyingItem { Id = 1, Price = 10m },
new BuyingItem { Id = 2, Price = 20m }
);
// Make sure that the above entities are created in the database
context.SaveChanges();
var p1 = context.Parcels.Find(1);
// Uncomment the following line if you are not using lazy loading.
//context.Entry(p1).Collection(p => p.Items).Load();
var p2 = context.Parcels.Find(2);
// Uncomment the following line if you are not using lazy loading.
//context.Entry(p2).Collection(p => p.Items).Load();
var i1 = context.BuyingItems.Find(1);
var i2 = context.BuyingItems.Find(2);
p1.Items.Add(i1);
p1.Items.Add(i2);
// Uncomment to test whether this fails or not, it will work, and guess what, no duplicates!!!
//p1.Items.Add(i1);
//p1.Items.Add(i1);
//p1.Items.Add(i1);
//p1.Items.Add(i1);
//p1.Items.Add(i1);
p2.Items.Add(i1);
p2.Items.Add(i2);
// The following WON'T work, since we're assigning a new collection, it'll try to insert duplicate values only to fail.
//p1.Items = new[] { i1, i2 };
//p2.Items = new[] { i2 };
}
ここでは、Seed
メソッド内でcontext.SaveChanges()
を呼び出すことにより、エンティティがデータベースで作成または更新されていることを確認します。その後、context
を使用して、必要な小包を取得し、アイテムオブジェクトを購入します。その後、Items
オブジェクトのParcel
プロパティ(コレクション)を使用して、必要に応じてBuyingItem
を追加します。
同じアイテムオブジェクトを使用してAdd
メソッドを何度呼び出しても、主キー違反が発生することはないことに注意してください。これは、EFが内部でHashSet<T>
を使用してParcel.Items
コレクションを管理しているためです。 HashSet<Item>
は、その性質上、重複するアイテムを追加することはできません。
さらに、例で示したように、このEF動作を回避するために何らかの方法で管理する場合、主キーは重複を許可しません。
AddOrUpdate
メソッドで識別子式として一般的なIdフィールド(int、identity)を使用する場合は、注意が必要です。
この場合、Parcelテーブルから行の1つを手動で削除すると、Seedメソッドを実行するたびに重複が作成されます(私が提供した更新されたSeed
メソッドを使用しても)上記)。
次のコードを検討してください。
context.Parcels.AddOrUpdate(
p => p.Id,
new Parcel { Id = 1, Description = "Parcel 1", Weight = 1.0 },
new Parcel { Id = 2, Description = "Parcel 1", Weight = 1.0 },
new Parcel { Id = 3, Description = "Parcel 1", Weight = 1.0 }
);
技術的には(ここで代理IDを考慮すると)、行は一意ですが、エンドユーザーの観点からは重複しています。
ここでの真の解決策は、識別子式としてDescription
フィールドを使用することです。この属性をDescription
クラスのParcel
プロパティに追加して、一意にする[MaxLength(255), Index(IsUnique=true)]
にします。 Seed
メソッドの次のスニペットを更新します。
context.Parcels.AddOrUpdate(
p => p.Description,
new Parcel { Description = "Parcel 1", Weight = 1.0 },
new Parcel { Description = "Parcel 2", Weight = 2.0 },
new Parcel { Description = "Parcel 3", Weight = 3.0 }
);
// Make sure that the above entities are created in the database
context.SaveChanges();
var p1 = context.Parcels.Single(p => p.Description == "Parcel 1");
EFは行の挿入中にそれを無視するため、私はId
フィールドを使用していないことに注意してください。また、Description
の値に関係なく、Id
を使用して正しいパーセルオブジェクトを取得しています。
古い答え
ここにいくつかの所見を追加したいと思います。
Id列がデータベースで生成されたフィールドである場合、Idを使用してもおそらく効果はありません。 EFはそれを無視します。
このメソッドは、Seedメソッドを1回実行すると正常に機能しているようです。ただし、2回実行しても重複は作成されません(ほとんどの場合、これを実行する必要があります)。多くの場合)、重複を注入する可能性があります。私の場合はそうしました。
このチュートリアル トム・ダイクストラによる正しいやり方を教えてくれました。当たり前のことを何も考えていないので、うまくいきます。 IDは指定しません。代わりに、既知の一意のキーを使用してコンテキストをクエリし、関連するエンティティ(コンテキストをクエリすることで取得されます)をそれらに追加します。私の場合、それは魅力のように機能しました。
OK。私はその状況でどうあるべきかを理解しています:
protected override void Seed(Context context)
{
base.Seed(context);
var buyingItems = new[]
{
new BuyingItem
{
Id = 1,
Price = 10m
},
new BuyingItem
{
Id = 2,
Price = 20m,
}
}
context.AddOrUpdate(new Parcel()
{
Id = 1,
Description = "Test",
Items = new List<BuyingItem>
{
buyingItems[0],
buyingItems[1]
}
},
new Parcel()
{
Id = 2,
Description = "Test2",
Items = new List<BuyingItem>
{
buyingItems[0],
buyingItems[1]
}
});
context.SaveChanges();
}
データベースに重複はありません。
ありがとう、ラディスラフ、あなたは私の仕事の解決策を見つけるための正しいベクトルを私にくれました。