Hanya sebagai tambahan (pada jawaban yang ditandai) ada perbedaan penting antara context.Entry(entity).State = EntityState.Unchanged
dan context.Attach(entity)
(dalam EF Core):
Saya melakukan beberapa tes untuk lebih memahaminya sendiri (oleh karena itu ini juga termasuk beberapa pengujian referensi umum), jadi ini adalah skenario pengujian saya:
- Saya menggunakan EF Core 3.1.3
- Saya dulu
QueryTrackingBehavior.NoTracking
- Saya hanya menggunakan atribut untuk pemetaan (lihat di bawah)
- Saya menggunakan konteks yang berbeda untuk mendapatkan pesanan dan memperbarui pesanan
- Saya menghapus seluruh db untuk setiap tes
Ini adalah modelnya:
public class Order
{
public int Id { get; set; }
public string Comment { get; set; }
public string ShippingAddress { get; set; }
public DateTime? OrderDate { get; set; }
public List<OrderPos> OrderPositions { get; set; }
[ForeignKey("OrderedByUserId")]
public User OrderedByUser { get; set; }
public int? OrderedByUserId { get; set; }
}
public class OrderPos
{
public int Id { get; set; }
public string ArticleNo { get; set; }
public int Quantity { get; set; }
[ForeignKey("OrderId")]
public Order Order { get; set; }
public int? OrderId { get; set; }
}
public class User
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
Ini adalah data uji (asli) dalam database:
Untuk mendapatkan pesanan:
order = db.Orders.Include(o => o.OrderPositions).Include(o => o.OrderedByUser).FirstOrDefault();
Sekarang tesnya:
Pembaruan Sederhana dengan EntityState :
db.Entry(order).State = EntityState.Unchanged;
order.ShippingAddress = "Germany"; // will be UPDATED
order.OrderedByUser.FirstName = "William (CHANGED)"; // will be IGNORED
order.OrderPositions[0].ArticleNo = "K-1234 (CHANGED)"; // will be IGNORED
order.OrderPositions.Add(new OrderPos { ArticleNo = "T-5555 (NEW)", Quantity = 5 }); // will be INSERTED
db.SaveChanges();
// Will generate SQL in 2 Calls:
// INSERT INTO [OrderPositions] ([ArticleNo], [OrderId], [Quantity]) VALUES ('T-5555', 1, 5)
// UPDATE [Orders] SET [ShippingAddress] = 'Germany' WHERE [Id] = 1
Pembaruan Sederhana dengan Lampirkan :
db.Attach(order);
order.ShippingAddress = "Germany"; // will be UPDATED
order.OrderedByUser.FirstName = "William (CHANGED)"; // will be UPDATED
order.OrderPositions[0].ArticleNo = "K-1234 (CHANGED)"; // will be UPDATED
order.OrderPositions.Add(new OrderPos { ArticleNo = "T-5555 (NEW)", Quantity = 5 }); // will be INSERTED
db.SaveChanges();
// Will generate SQL in 1 Call:
// UPDATE [OrderPositions] SET [ArticleNo] = 'K-1234' WHERE [Id] = 1
// INSERT INTO [OrderPositions] ([ArticleNo], [OrderId], [Quantity]) VALUES ('T-5555 (NEW)', 1, 5)
// UPDATE [Orders] SET [ShippingAddress] = 'Germany' WHERE [Id] = 1
// UPDATE [Users] SET [FirstName] = 'William (CHANGED)' WHERE [Id] = 1
Perbarui dengan mengubah Id-Anak dengan EntityState :
db.Entry(order).State = EntityState.Unchanged;
order.ShippingAddress = "Germany"; // will be UPDATED
order.OrderedByUser.Id = 3; // will be IGNORED
order.OrderedByUser.FirstName = "William (CHANGED)"; // will be IGNORED
order.OrderPositions[0].Id = 3; // will be IGNORED
order.OrderPositions[0].ArticleNo = "K-1234 (CHANGED)"; // will be IGNORED
order.OrderPositions.Add(new OrderPos { ArticleNo = "T-5555 (NEW)", Quantity = 5 }); // will be INSERTED
db.SaveChanges();
// Will generate SQL in 2 Calls:
// INSERT INTO [OrderPositions] ([ArticleNo], [OrderId], [Quantity]) VALUES ('T-5555', 1, 5)
// UPDATE [Orders] SET [ShippingAddress] = 'Germany' WHERE [Id] = 1
Perbarui dengan mengubah Id Anak dengan Lampirkan :
db.Attach(order);
order.ShippingAddress = "Germany"; // would be UPDATED
order.OrderedByUser.Id = 3; // will throw EXCEPTION
order.OrderedByUser.FirstName = "William (CHANGED)"; // would be UPDATED
order.OrderPositions[0].Id = 3; // will throw EXCEPTION
order.OrderPositions[0].ArticleNo = "K-1234 (CHANGED)"; // would be UPDATED
order.OrderPositions.Add(new OrderPos { ArticleNo = "T-5555 (NEW)", Quantity = 5 }); // would be INSERTED
db.SaveChanges();
// Throws Exception: The property 'Id' on entity type 'User' is part of a key and so cannot be modified or marked as modified. To change the principal of an existing entity with an identifying foreign key first delete the dependent and invoke 'SaveChanges' then associate the dependent with the new principal.)
Catatan: Ini melempar Exception, tidak peduli apakah Id diubah atau disetel ke nilai asli, sepertinya status Id disetel ke "diubah" dan ini tidak diperbolehkan (karena itu kunci utama)
Perbarui dengan mengubah ID Anak sebagai yang baru (tidak ada perbedaan antara EntityState dan Attach):
db.Attach(order); // or db.Entry(order).State = EntityState.Unchanged;
order.OrderedByUser = new User();
order.OrderedByUser.Id = 3; // // Reference will be UPDATED
order.OrderedByUser.FirstName = "William (CHANGED)"; // will be UPDATED (on User 3)
db.SaveChanges();
// Will generate SQL in 2 Calls:
// UPDATE [Orders] SET [OrderedByUserId] = 3, [ShippingAddress] = 'Germany' WHERE [Id] = 1
// UPDATE [Users] SET [FirstName] = 'William (CHANGED)' WHERE [Id] = 3
Catatan: Lihat perbedaan Update dengan EntityState tanpa new (di atas). Kali ini Nama akan diperbarui, karena contoh Pengguna baru.
Perbarui dengan mengubah Reference-Id dengan EntityState :
db.Entry(order).State = EntityState.Unchanged;
order.ShippingAddress = "Germany"; // will be UPDATED
order.OrderedByUserId = 3; // will be UPDATED
order.OrderedByUser.Id = 2; // will be IGNORED
order.OrderedByUser.FirstName = "William (CHANGED)"; // will be IGNORED
order.OrderPositions[0].Id = 3; // will be IGNORED
order.OrderPositions[0].ArticleNo = "K-1234 (CHANGED)"; // will be IGNORED
order.OrderPositions.Add(new OrderPos { ArticleNo = "T-5555 (NEW)", Quantity = 5 }); // will be INSERTED
db.SaveChanges();
// Will generate SQL in 2 Calls:
// INSERT INTO [OrderPositions] ([ArticleNo], [OrderId], [Quantity]) VALUES ('T-5555', 1, 5)
// UPDATE [Orders] SET [OrderedByUserId] = 3, [ShippingAddress] = 'Germany' WHERE [Id] = 1
Perbarui dengan mengubah Referensi-Id dengan Lampirkan :
db.Attach(order);
order.ShippingAddress = "Germany"; // will be UPDATED
order.OrderedByUserId = 3; // will be UPDATED
order.OrderedByUser.FirstName = "William (CHANGED)"; // will be UPDATED (on FIRST User!)
order.OrderPositions[0].ArticleNo = "K-1234 (CHANGED)"; // will be UPDATED
order.OrderPositions.Add(new OrderPos { ArticleNo = "T-5555 (NEW)", Quantity = 5 }); // will be INSERTED
db.SaveChanges();
// Will generate SQL in 1 Call:
// UPDATE [OrderPositions] SET [ArticleNo] = 'K-1234' WHERE [Id] = 1
// INSERT INTO [OrderPositions] ([ArticleNo], [OrderId], [Quantity]) VALUES ('T-5555 (NEW)', 1, 5)
// UPDATE [Orders] SET [OrderedByUserId] = 3, [ShippingAddress] = 'Germany' WHERE [Id] = 1
// UPDATE [Users] SET [FirstName] = 'William (CHANGED)' WHERE [Id] = 1
Catatan: Referensi akan diubah menjadi Pengguna 3, tetapi juga pengguna 1 akan diperbarui, saya kira ini karena order.OrderedByUser.Id
tidak berubah (masih 1).
Kesimpulan
Dengan EntityState Anda memiliki kontrol lebih, tetapi Anda harus memperbarui sub-properti (tingkat kedua) sendiri. Dengan Lampirkan Anda dapat memperbarui semuanya (saya kira dengan semua tingkat properti), tetapi Anda harus tetap memperhatikan referensi. Sebagai contoh: Jika User (OrderedByUser) akan menjadi dropDown, mengubah nilai melalui dropDown mungkin menimpa seluruh objek User. Dalam kasus ini, dropDown-Value asli akan ditimpa, bukan referensi.
Bagi saya kasus terbaik adalah mengatur objek seperti OrderedByUser ke null dan hanya mengatur order.OrderedByUserId ke nilai baru, jika saya hanya ingin mengubah referensi (tidak peduli apakah EntityState atau Attach).
Semoga ini bisa membantu, saya tahu teksnya banyak: D