ใครบ้างมีคำแนะนำเกี่ยวกับวิธีที่มีประสิทธิภาพที่สุดในการใช้ตรรกะ "แถวปรับปรุงถ้ามันมีอยู่แทรก" ตรรกะโดยใช้ Entity Framework?
ใครบ้างมีคำแนะนำเกี่ยวกับวิธีที่มีประสิทธิภาพที่สุดในการใช้ตรรกะ "แถวปรับปรุงถ้ามันมีอยู่แทรก" ตรรกะโดยใช้ Entity Framework?
คำตอบ:
หากคุณกำลังทำงานกับวัตถุที่แนบมา (วัตถุที่โหลดจากอินสแตนซ์เดียวกันของบริบท) คุณสามารถใช้:
if (context.ObjectStateManager.GetObjectStateEntry(myEntity).State == EntityState.Detached)
{
context.MyEntities.AddObject(myEntity);
}
// Attached object tracks modifications automatically
context.SaveChanges();
หากคุณสามารถใช้ความรู้ใด ๆ เกี่ยวกับคีย์ของวัตถุคุณสามารถใช้สิ่งนี้:
if (myEntity.Id != 0)
{
context.MyEntities.Attach(myEntity);
context.ObjectStateManager.ChangeObjectState(myEntity, EntityState.Modified);
}
else
{
context.MyEntities.AddObject(myEntity);
}
context.SaveChanges();
หากคุณไม่สามารถตัดสินใจว่าจะมีอยู่จริงของวัตถุด้วยรหัสหรือไม่คุณต้องออกจากการค้นหา:
var id = myEntity.Id;
if (context.MyEntities.Any(e => e.Id == id))
{
context.MyEntities.Attach(myEntity);
context.ObjectStateManager.ChangeObjectState(myEntity, EntityState.Modified);
}
else
{
context.MyEntities.AddObject(myEntity);
}
context.SaveChanges();
using
บล็อกสั้น ๆ การปล่อยให้บริบทอยู่ในความทรงจำชั่วครู่หนึ่งหรือไม่ ตัวอย่างเช่นในช่วงชีวิตของรูปแบบ Windows? ปกติแล้วฉันจะพยายามทำความสะอาดวัตถุฐานข้อมูลเพื่อให้แน่ใจว่าภาระขั้นต่ำในฐานข้อมูล ไม่มีปัญหาในการทำลายบริบทของ EF หรือไม่?
ในฐานะของ Entity Framework 4.3 มีAddOrUpdate
เมธอดที่เนมสเปซSystem.Data.Entity.Migrations
:
public static void AddOrUpdate<TEntity>(
this IDbSet<TEntity> set,
params TEntity[] entities
)
where TEntity : class
ซึ่งโดยเอกสาร :
เพิ่มหรืออัพเดทเอนทิตีตามคีย์เมื่อมีการเรียก SaveChanges เทียบเท่ากับการดำเนินการ "เพิ่ม" จากคำศัพท์ฐานข้อมูล วิธีนี้มีประโยชน์เมื่อทำการ seed ข้อมูลโดยใช้ Migrations
เพื่อตอบความคิดเห็นโดย @ Smashing1978ฉันจะวางส่วนที่เกี่ยวข้องจากลิงก์ที่ @Colin จัดหาให้
งานของ AddOrUpdate คือเพื่อให้แน่ใจว่าคุณจะไม่สร้างข้อมูลซ้ำเมื่อคุณเก็บข้อมูลระหว่างการพัฒนา
ก่อนอื่นมันจะดำเนินการสืบค้นในฐานข้อมูลของคุณเพื่อค้นหาระเบียนที่สิ่งที่คุณให้ไว้เป็นคีย์ (พารามิเตอร์แรก) ตรงกับค่าคอลัมน์ที่แมป (หรือค่า) ที่ให้ไว้ใน AddOrUpdate ดังนั้นนี่คือห่านตัวเล็ก ๆ ที่หลวมสำหรับการจับคู่ แต่ก็ดีอย่างสมบูรณ์แบบสำหรับการสร้างข้อมูลเวลาการออกแบบ
ที่สำคัญกว่านั้นหากพบการแข่งขันการอัปเดตจะอัปเดตทั้งหมดและลบล้างสิ่งที่ไม่ได้อยู่ใน AddOrUpdate ของคุณ
ที่กล่าวว่าฉันมีสถานการณ์ที่ฉันดึงข้อมูลจากบริการภายนอกและแทรกหรือปรับปรุงค่าที่มีอยู่โดยคีย์หลัก (และข้อมูลท้องถิ่นของฉันสำหรับผู้บริโภคเป็นแบบอ่านอย่างเดียว) - ใช้AddOrUpdate
ในการผลิตมานานกว่า 6 เดือนแล้ว ไม่มีปัญหา
ความมหัศจรรย์ที่เกิดขึ้นเมื่อเรียกและขึ้นอยู่กับปัจจุบันSaveChanges()
EntityState
หากเอนทิตีมีEntityState.Added
, มันจะถูกเพิ่มไปยังฐานข้อมูล, ถ้ามันมีEntityState.Modified
, มันจะถูกอัพเดตในฐานข้อมูล ดังนั้นคุณสามารถใช้InsertOrUpdate()
วิธีดังต่อไปนี้:
public void InsertOrUpdate(Blog blog)
{
using (var context = new BloggingContext())
{
context.Entry(blog).State = blog.BlogId == 0 ?
EntityState.Added :
EntityState.Modified;
context.SaveChanges();
}
}
เพิ่มเติมเกี่ยวกับ EntityState
ถ้าคุณไม่สามารถตรวจสอบId = 0
เพื่อตรวจสอบว่ามันเป็นองค์กรใหม่หรือไม่ตรวจสอบคำตอบของ Ladislav Mrnka
หากคุณรู้ว่าคุณกำลังใช้บริบทเดียวกันและไม่ถอดเอนทิตี้ใด ๆ ออกไปคุณสามารถสร้างเวอร์ชันทั่วไปดังนี้:
public void InsertOrUpdate<T>(T entity, DbContext db) where T : class
{
if (db.Entry(entity).State == EntityState.Detached)
db.Set<T>().Add(entity);
// If an immediate save is needed, can be slow though
// if iterating through many entities:
db.SaveChanges();
}
db
สามารถแน่นอนเป็นเขตข้อมูลชั้นเรียนหรือวิธีการที่สามารถทำให้คงที่และส่วนขยาย แต่นี่คือพื้นฐาน
คำตอบของ Ladislav นั้นใกล้เคียงกัน แต่ฉันต้องทำการแก้ไขสองสามครั้งเพื่อให้มันทำงานใน EF6 (ฐานข้อมูลก่อน) ฉันขยายบริบทข้อมูลของฉันด้วยวิธีการ AddOrUpdate ของฉันและจนถึงขณะนี้สิ่งนี้ดูเหมือนจะทำงานได้ดีกับวัตถุที่แยกออกมา:
using System.Data.Entity;
[....]
public partial class MyDBEntities {
public void AddOrUpdate(MyDBEntities ctx, DbSet set, Object obj, long ID) {
if (ID != 0) {
set.Attach(obj);
ctx.Entry(obj).State = EntityState.Modified;
}
else {
set.Add(obj);
}
}
[....]
ในความคิดของฉันมันคุ้มค่าที่จะบอกว่าด้วยEntityGraphOperations สำหรับ Entity Framework Code ที่เปิดตัวครั้งแรกคุณสามารถช่วยตัวเองจากการเขียนรหัสซ้ำ ๆ เพื่อกำหนดสถานะของเอนทิตีทั้งหมดในกราฟ ฉันเป็นผู้เขียนผลิตภัณฑ์นี้ และฉันได้รับการตีพิมพ์ในGitHub , รหัสโครงการ ( รวมถึงการสาธิตขั้นตอนโดยขั้นตอนและโครงการตัวอย่างที่มีความพร้อมสำหรับการดาวน์โหลด)และnuget
มันจะโดยอัตโนมัติตั้งรัฐของหน่วยงานที่ไปหรือAdded
Modified
และคุณจะต้องเลือกเอนทิตีที่ต้องลบหากไม่มีอยู่อีกต่อไป
ตัวอย่าง:
สมมติว่าฉันได้รับPerson
วัตถุ Person
อาจมีโทรศัพท์หลายเครื่องเอกสารและอาจมีคู่สมรส
public class Person
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string MiddleName { get; set; }
public int Age { get; set; }
public int DocumentId {get; set;}
public virtual ICollection<Phone> Phones { get; set; }
public virtual Document Document { get; set; }
public virtual PersonSpouse PersonSpouse { get; set; }
}
ฉันต้องการกำหนดสถานะของเอนทิตีทั้งหมดที่รวมอยู่ในกราฟ
context.InsertOrUpdateGraph(person)
.After(entity =>
{
// Delete missing phones.
entity.HasCollection(p => p.Phones)
.DeleteMissingEntities();
// Delete if spouse is not exist anymore.
entity.HasNavigationalProperty(m => m.PersonSpouse)
.DeleteIfNull();
});
เช่นเดียวกับที่คุณทราบคุณสมบัติที่สำคัญที่ไม่ซ้ำกันสามารถมีบทบาทในขณะที่การกำหนดสถานะของเอนทิตี้โทรศัพท์ เพื่อวัตถุประสงค์พิเศษเช่นเรามีระดับซึ่งสืบทอดมาจากExtendedEntityTypeConfiguration<>
EntityTypeConfiguration<>
ถ้าเราต้องการที่จะใช้การกำหนดค่าพิเศษเช่นนั้นเราจะต้องสืบทอดการเรียนการทำแผนที่ของเราจากมากกว่าExtendedEntityTypeConfiguration<>
EntityTypeConfiguration<>
ตัวอย่างเช่น:
public class PhoneMap: ExtendedEntityTypeConfiguration<Phone>
{
public PhoneMap()
{
// Primary Key
this.HasKey(m => m.Id);
…
// Unique keys
this.HasUniqueKey(m => new { m.Prefix, m.Digits });
}
}
นั่นคือทั้งหมดที่
ใส่อย่างอื่นปรับปรุงทั้งสอง
public void InsertUpdateData()
{
//Here TestEntities is the class which is given from "Save entity connection setting in web.config"
TestEntities context = new TestEntities();
var query = from data in context.Employee
orderby data.name
select data;
foreach (Employee details in query)
{
if (details.id == 1)
{
//Assign the new values to name whose id is 1
details.name = "Sanjay";
details. Surname="Desai";
details.address=" Desiwadi";
}
else if(query==null)
{
details.name="Sharad";
details.surname=" Chougale ";
details.address=" Gargoti";
}
}
//Save the changes back to database.
context.SaveChanges();
}
ตรวจสอบแถวที่มีอยู่ด้วยใด ๆ
public static void insertOrUpdateCustomer(Customer customer)
{
using (var db = getDb())
{
db.Entry(customer).State = !db.Customer.Any(f => f.CustomerId == customer.CustomerId) ? EntityState.Added : EntityState.Modified;
db.SaveChanges();
}
}
ทางเลือกสำหรับคำตอบ @LadislavMrnka สิ่งนี้หาก Entity Framework 6.2.0
หากคุณมีรายการที่เฉพาะเจาะจงDbSet
และรายการที่จำเป็นต้องได้รับการปรับปรุงหรือสร้าง:
var name = getNameFromService();
var current = _dbContext.Names.Find(name.BusinessSystemId, name.NameNo);
if (current == null)
{
_dbContext.Names.Add(name);
}
else
{
_dbContext.Entry(current).CurrentValues.SetValues(name);
}
_dbContext.SaveChanges();
อย่างไรก็ตามสิ่งนี้ยังสามารถใช้สำหรับทั่วไปที่DbSet
มีคีย์หลักเดียวหรือคีย์หลักคอมโพสิต
var allNames = NameApiService.GetAllNames();
GenericAddOrUpdate(allNames, "BusinessSystemId", "NameNo");
public virtual void GenericAddOrUpdate<T>(IEnumerable<T> values, params string[] keyValues) where T : class
{
foreach (var value in values)
{
try
{
var keyList = new List<object>();
//Get key values from T entity based on keyValues property
foreach (var keyValue in keyValues)
{
var propertyInfo = value.GetType().GetProperty(keyValue);
var propertyValue = propertyInfo.GetValue(value);
keyList.Add(propertyValue);
}
GenericAddOrUpdateDbSet(keyList, value);
//Only use this when debugging to catch save exceptions
//_dbContext.SaveChanges();
}
catch
{
throw;
}
}
_dbContext.SaveChanges();
}
public virtual void GenericAddOrUpdateDbSet<T>(List<object> keyList, T value) where T : class
{
//Get a DbSet of T type
var someDbSet = Set(typeof(T));
//Check if any value exists with the key values
var current = someDbSet.Find(keyList.ToArray());
if (current == null)
{
someDbSet.Add(value);
}
else
{
Entry(current).CurrentValues.SetValues(value);
}
}
การแก้ไข
public static void InsertOrUpdateRange<T, T2>(this T entity, List<T2> updateEntity)
where T : class
where T2 : class
{
foreach(var e in updateEntity)
{
context.Set<T2>().InsertOrUpdate(e);
}
}
public static void InsertOrUpdate<T, T2>(this T entity, T2 updateEntity)
where T : class
where T2 : class
{
if (context.Entry(updateEntity).State == EntityState.Detached)
{
if (context.Set<T2>().Any(t => t == updateEntity))
{
context.Set<T2>().Update(updateEntity);
}
else
{
context.Set<T2>().Add(updateEntity);
}
}
context.SaveChanges();
}