ในบทความก่อนหน้านี้ ฉันนำเสนอปัญหาหลายประการที่คุณอาจพบเมื่อใช้ LINQ กับ SQL สำหรับการดำเนินการอัปเดต อันที่จริงนี่ไม่ใช่ปัญหาที่ฉันพบเพียงลำพัง เมื่อฉันค้นหาคำตอบบนอินเทอร์เน็ต ฉันพบว่ามีคนจำนวนมากตีพิมพ์บทความที่คล้ายกันในหัวข้อนี้ แต่สิ่งที่ฉันไม่พอใจคือแม้ว่าพวกเขาจะหยิบยกปัญหาขึ้นมา แต่พวกเขาไม่ได้ทำการวิเคราะห์โดยละเอียดเท่านั้น พวกเขาให้วิธีแก้ปัญหาเท่านั้น (เช่น การเพิ่มคอลัมน์ RowVersion การลบการเชื่อมโยง ฯลฯ) แต่ไม่ได้อธิบายว่าทำไมพวกเขาจึงต้องทำเช่นนี้ . นี่เป็นความตั้งใจเดิมของการเขียนบทความก่อนหน้านี้ ฉันหวังว่าจะค้นหาวิธีแก้ไขปัญหาทีละขั้นตอนผ่านการวิเคราะห์ซอร์สโค้ด LINQ เป็น SQL บทความนี้จะกล่าวถึงวิธีการเหล่านี้ทีละรายการ
ตัวเลือกที่ 1: การมอบหมายใหม่ ในเฟรมเวิร์กโอเพ่นซอร์ส Ezsocio โดย TerryLee, Anytao, Ding Xue และอื่นๆ จะมีการปรับใช้การมอบหมายใหม่ในบางสถานที่ ภายในวิธีการอัปเดต ให้รับเอนทิตีในฐานข้อมูลตามคีย์หลัก จากนั้นกำหนดค่าให้กับคุณสมบัติทีละรายการด้วยเอนทิตีในพารามิเตอร์
โมฆะสาธารณะ UpdateProfile (โปรไฟล์ p)
-
การใช้ (RepositoryContext db = RepositoryContext() ใหม่)
-
โปรไฟล์ var = db.GetTable<โปรไฟล์>().First<โปรไฟล์>(u => u.ID == p.ID);
profile.Birthday = วันเกิด;
profile.Gender = p.เพศ;
profile.Hometown = p.บ้านเกิด;
โปรไฟล์.MSN = p.MSN;
profile.NickName = p.NickName;
โปรไฟล์ PhoneNumber = p.PhoneNumber;
โปรไฟล์.QQ = p.QQ;
โปรไฟล์.สถานะ = p.สถานะ;
profile.TrueName = p.TrueName;
profile.StateRefreshTime = p.StateRefreshTime;
profile.Avatar = p.อวาตาร์;
profile.Website = p.เว็บไซต์;
db.ส่งการเปลี่ยนแปลง();
-
-
บราเดอร์ Yang Guo ยังจัดเตรียมวิธีการสะท้อนกลับสำหรับโซลูชันนี้เพื่อให้สามารถคัดลอกค่าแอตทริบิวต์ได้โดยอัตโนมัติ
แต่โดยส่วนตัวแล้วฉันคิดว่านี่เป็นโครงการที่หลีกเลี่ยงความเป็นจริงและหลีกเลี่ยงความเป็นจริง มันไม่ได้ใช้ API ที่ LINQ มอบให้กับ SQL สำหรับการดำเนินการอัปเดต แต่ใช้กลยุทธ์วงเวียน นี่เป็นการประนีประนอมจริงๆ เป็นเพราะวิธีการแนบนั้น "ใช้งานไม่ได้ง่าย" ดังนั้นเราจึงไม่ใช้มันใช่ไหม อิอิ
ตัวเลือกที่ 2: ปิดใช้งานการติดตามวัตถุ ในเรื่องนี้ lea เสนอว่าการอัปเดตที่ถูกต้องสามารถทำได้โดยการตั้งค่าคุณสมบัติ ObjectTrackingEnabled ของ DataContext เป็นเท็จ
สินค้าสาธารณะ GetProduct(int id)
-
NorthwindDataContext db = NorthwindDataContext ใหม่ ();
db.ObjectTrackingEnabled = เท็จ;
กลับ db.Products.SingleOrDefault(p => p.ProductID == id);
-
ไม่มีการเปลี่ยนแปลงรหัสอื่น ๆ
เหตุใดจึงสามารถอัปเดตได้ตามปกติหลังจากปิดใช้งานการติดตามวัตถุ มาหาคำตอบจากซอร์สโค้ดกัน
บูลสาธารณะ ObjectTrackingEnabled
-
รับ
-
นี้.CheckDispose();
ส่งคืนสิ่งนี้ objectTrackingEnabled;
-
ชุด
-
นี้.CheckDispose();
ถ้า (this.Services.HasCachedObjects)
-
โยน System.Data.Linq.Error.OptionsCannotBeModifiedAfterQuery();
-
this.objectTrackingEnabled = ค่า;
ถ้า (!this.objectTrackingEnabled)
-
this.deferredLoadingEnabled = เท็จ;
-
นี้.บริการ.ResetServices();
-
-
ปรากฎว่าเมื่อ ObjectTrackingEnabled ถูกตั้งค่าเป็นเท็จ DeferredLoadingEnabled จะถูกตั้งค่าเป็นเท็จในเวลาเดียวกัน ด้วยวิธีนี้ เมื่อดำเนินการแบบสอบถาม จะไม่มีการโหลดข้อมูลที่ต้องใช้แบบสอบถามที่ล่าช้าสำหรับเอนทิตี ดังนั้นจึงไม่มีข้อยกเว้นเกิดขึ้นในระหว่างการแนบ (ดูการวิเคราะห์ในบทความก่อนหน้า)
ใน MSDN เรายังได้รับข้อมูลที่เป็นประโยชน์ต่อไปนี้: การตั้งค่าคุณสมบัติ ObjectTrackingEnable เป็นเท็จสามารถปรับปรุงประสิทธิภาพการดึงข้อมูลได้เนื่องจากจะช่วยลดจำนวนรายการที่จะติดตาม นี่เป็นคุณสมบัติที่น่าดึงดูดมาก
อย่างไรก็ตาม เมื่อปิดใช้งานการติดตามวัตถุ ควรให้ความสนใจเป็นพิเศษในสองประเด็น: (1) จะต้องปิดใช้งานก่อนที่จะดำเนินการค้นหา (2) หลังจากถูกปิดใช้งานแล้ว จะไม่สามารถเรียกเมธอดแนบและส่งการเปลี่ยนแปลงได้อีกต่อไป มิฉะนั้นจะเกิดข้อยกเว้น
ตัวเลือกที่ 3: ลบการเชื่อมโยง วิธีการแบบง่อยได้รับการแนะนำในบทความก่อนหน้านี้ ซึ่งก็คือการตั้งค่าหมวดหมู่ที่เกี่ยวข้องกับผลิตภัณฑ์เป็นโมฆะด้วยตนเองในวิธีการ GetProduct เราสามารถแตกโค้ดส่วนนี้ออกมาและใส่ไว้ในวิธี Detach ได้ เนื่องจาก Detach นี้เป็นวิธีการเอนทิตี จึงสามารถใช้คลาสบางส่วนได้:
ผลิตภัณฑ์สาธารณะบางส่วนคลาส
-
โมฆะสาธารณะแยกออก ()
-
this._Category = ค่าเริ่มต้น (EntityRef <หมวดหมู่>);
-
-
หมวดหมู่คลาสสาธารณะบางส่วน
-
โมฆะสาธารณะแยกออก ()
-
foreach (ผลิตภัณฑ์ var ใน this.Products)
-
สินค้าถอด();
-
-
}แต่วิธีการกำหนด Detach สำหรับแต่ละเอนทิตีนี้ยุ่งยากเกินไป เมื่อจำนวนเอนทิตีเพิ่มขึ้น ความสัมพันธ์จะซับซ้อนมากขึ้นเรื่อยๆ และเป็นเรื่องง่ายที่แอตทริบิวต์ที่ขาดหายไปจะปรากฏขึ้น จาง อี้ เสนอวิธีการที่หรูหรามากในการสรุปตรรกะนี้โดยใช้การไตร่ตรอง:
โมฆะส่วนตัวแยกออก (เอนทิตี TEntity)
-
foreach (FieldInfo fi ในเอนทิตี GetType().GetFields(BindingFlags.NonPublic | BindingFlags.Instance))
-
ถ้า (fi.FieldType.ToString().Contains("EntityRef"))
-
ค่า var = fi.GetValue (เอนทิตี);
ถ้า (ค่า != null)
-
fi.SetValue (เอนทิตี, null);
-
-
ถ้า (fi.FieldType.ToString().Contains("EntitySet"))
-
ค่า var = fi.GetValue (เอนทิตี);
ถ้า (ค่า != null)
-
MethodInfo mi = value.GetType().GetMethod("ล้าง");
ถ้า (mi != null)
-
mi. เรียกใช้ (ค่า, null);
-
fi.SetValue (เอนทิตี, มูลค่า);
-
-
-
-
บางคนยังคิดว่าเหตุการณ์ PropertyChanging และ PropertyChanged ควรตั้งค่าเป็น null ในระหว่างการแยกออก แต่แนวคิดโดยรวมยังคงเหมือนเดิม
ตัวเลือกที่ 4: ใช้การมอบหมาย นี่คือวิธีการที่กำหนดโดย ZC29 ในความคิดเห็นของบทความล่าสุดของฉัน โดยส่วนตัวแล้วฉันคิดว่ามันคุ้มค่าที่จะเรียนรู้
โมฆะสาธารณะ UpdateProductWithDelegate (นิพจน์<Func<Product, bool>> เพรดิเคต การดำเนินการ <ผลิตภัณฑ์>)
-
NorthwindDataContext db = NorthwindDataContext ใหม่ ();
ผลิตภัณฑ์ var = db.Products.SingleOrDefault (ภาคแสดง);
การกระทำ(ผลิตภัณฑ์);
db.ส่งการเปลี่ยนแปลง();
-
//รหัสลูกค้า
พื้นที่เก็บข้อมูล ProductRepository = ใหม่ ProductRepository();
repository.UpdateProductWithDelegate(p => p.ProductID == 1, p =>
-
p.ProductName = "เปลี่ยนแปลง";
-
ใช้นิพจน์ Lambda เพื่อฝังตรรกะ GetProduct ลงใน UpdateProduct และใช้ผู้รับมอบสิทธิ์เพื่อเลื่อนการดำเนินการของตรรกะการอัปเดต วิธีนี้จะใส่การค้นหาและอัปเดตลงใน DataContext อย่างชาญฉลาด ดังนั้นจึงข้ามการแนบ อย่างไรก็ตาม API ของวิธีนี้ซับซ้อนเกินไปเล็กน้อย และต้องใช้โปรแกรมเมอร์ไคลเอนต์ในระดับสูงเกินไป ยิ่งไปกว่านั้น จะต้องดำเนินการรับตรรกะอีกครั้งในการอัปเดต แม้ว่าการสูญเสียประสิทธิภาพจะน้อยมาก แต่ดูเหมือนว่าจะทำให้ผู้คนรู้สึกว่ายังไม่แห้งเพียงพอ
ตัวเลือกที่ 5: ใช้คำสั่ง UPDATE ในซอร์สโค้ดของ Ezsocio ฉันพบเมธอด RepositoryBase.UpdateEntity การประกบคำสั่ง SQL เสร็จสิ้นภายในเมธอด และเฉพาะคอลัมน์ที่เปลี่ยนแปลงเท่านั้นที่จะได้รับการอัปเดต เนื่องจาก ITable ไม่ได้ใช้ที่นี่อีกต่อไป และจำเป็นต้องมีการสนับสนุนเฟรมเวิร์กแบบเต็ม จึงไม่มีการแสดงความคิดเห็นเพิ่มเติม โปรดดูซอร์สโค้ดของ Ezsocio สำหรับรายละเอียด
สรุป บทความนี้แสดงรายการวิธีแก้ปัญหาหลายอย่างที่ฉันพบบนอินเทอร์เน็ตในช่วงไม่กี่วันที่ผ่านมา วิธีแก้ปัญหาเหล่านี้ล้วนมีข้อดีและข้อเสีย ขึ้นอยู่กับความคิดเห็นของแต่ละคน ในบทความถัดไป ฉันจะเปรียบเทียบประสิทธิภาพของวิธีการเหล่านี้เพื่อค้นหาวิธีแก้ปัญหาที่เหมาะสมที่สุด