สังเกต:
1. "เอนทิตี" ที่กล่าวถึงในบทความนี้สร้างขึ้นโดย LINQ TO SQL (เช่น .dbml)
2. คุณต้องเข้าใจว่า LINQ TO SQL ใช้การเชื่อมโยงตารางอย่างไร EntitySet และ EntityRef
บางทีเห็นชื่อเรื่องแล้วอาจจะคิดว่าคำถามค่อนข้างเป็นนามธรรม ผมขอยกตัวอย่างเพื่ออธิบายปัญหาโดยละเอียดครับ
ในสถาปัตยกรรม N-tier ที่ใช้ LINQ TO SQL หากเราจำเป็นต้องอัปเดตเอนทิตี กระบวนการควรเป็นดังนี้:
กระบวนการ
BLL.GetModel(p=>p.id==1) --> แก้ไขค่าแอตทริบิวต์ (ฟิลด์) ที่เกี่ยวข้อง --> BLL.Update(เอนทิตีเอนทิตี) --> DAL.Update(เอนทิตีเอนทิตี) --> อัปเดตสำเร็จ
ในเวลานี้ เราจำเป็นต้องส่งเอนทิตีนี้จากชั้นธุรกิจ (BLL) ไปยังชั้นการเข้าถึงข้อมูล (DAL) เมื่อวิธี GetModel ส่งคืนเอนทิตี DataContext จะถูกปล่อยทันที จากนั้นจะสร้างอินสแตนซ์อีกครั้งเมื่อ DAL ดำเนินการวิธีการอัปเดต (เอนทิตีเอนทิตี) สร้าง DataContext เพื่อดำเนินการอัปเดต จะเห็นได้ว่าเอนทิตีที่ส่งผ่านจาก DataContext แรกไปยัง DataContext อื่น เมื่อดำเนินการกับ DataContext ที่แตกต่างกันใน LINQ ไปยัง SQL จำเป็นต้องมีการดำเนินการเพิ่มเติม จะต้องดำเนินการก่อน นั่นคือ context.Entity. Attach(entity,true); ในที่สุด context.SubmitChanges();
มาดูโค้ดกัน:
รหัส
คลาส BLL
-
DAL แบบอ่านอย่างเดียวส่วนตัว dal = DAL ใหม่ ();
LinqToSqlProvider.User GetModel สาธารณะ (นิพจน์ <Func<LinqToSqlProvider.User, bool>> นิพจน์)
-
dal.GetModel(การแสดงออก);
-
การอัปเดตโมฆะสาธารณะ (เอนทิตี LinqToSqlProvider.User)
-
dal.Update (เอนทิตี);
-
-
คลาส DAL
-
LinqToSqlProvider.User GetModel สาธารณะ (นิพจน์ <Func<LinqToSqlProvider.User, bool>> นิพจน์)
-
รายการ LinqToSqlProvider.User = CriTextBroadcast.LinqToSqlProvider.User ใหม่ ();
การใช้ (บริบท CriTextBroadcastDBDataContext = DataContext)
-
รายการ = context.User.SingleOrDefault (นิพจน์);
-
ส่งคืน;
-
การอัปเดตโมฆะสาธารณะ (เอนทิตี LinqToSqlProvider.User)
-
การใช้ (บริบท CriTextBroadcastDBDataContext = DataContext)
-
context.User.Attach (รายการ, จริง);
บริบทส่งการเปลี่ยนแปลง ();
-
-
-
ที่จริงแล้ว เมื่อเราใช้โค้ดข้างต้นเพื่อดำเนินการ ข้อยกเว้นนี้จะเกิดขึ้น:
พยายามแนบหรือเพิ่มเอนทิตี เอนทิตีนี้ไม่ใช่เอนทิตีใหม่ อาจถูกโหลดจาก DataContext อื่น...
หลังจากตรวจสอบข้อมูลมากมายแต่ก็ไม่มีประโยชน์ ในที่สุดฉันก็เห็นคำถามที่คล้ายกันในโพสต์ต่างประเทศ ปรากฎว่าตารางที่สอดคล้องกับเอนทิตีนี้มีตารางที่เกี่ยวข้องหลายตาราง เช่น: ผู้ใช้ -> ข้อความ ผู้ใช้ -> รูปภาพ ฯลฯ
คุณลักษณะเช่น EntitySet<ข้อความ> จะถูกสร้างขึ้นโดยอัตโนมัติเมื่อสร้างคลาสเอนทิตี เนื่องจากเราไม่ได้อ่านเนื้อหาที่เกี่ยวข้องเหล่านี้ร่วมกันเมื่อได้รับเอนทิตี ObjectDisposedException จะเกิดขึ้นในแอตทริบิวต์เหล่านี้เมื่อแนบ (แนบ) นั่นเป็นเพราะ DataContext ใช้เพื่อสอบถามเอนทิตีนี้ได้รับการเผยแพร่แล้ว
สารละลาย:
รหัส
/// <สรุป>
/// เอนทิตี LinqToSql เสริมถูกแยกออกจาก DataContext ดั้งเดิม
/// </สรุป>
/// <typeparam name="TEntity"></typeparam>
/// <ชื่อพารามิเตอร์ = "เอนทิตี" ></ พารามิเตอร์>
โมฆะคงที่สาธารณะ Detatch <TEntity> (เอนทิตี TEntity)
-
พิมพ์ t = เอนทิตี GetType();
คุณสมบัติ System.Reflection.PropertyInfo[] = t.GetProperties(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance);
foreach (คุณสมบัติ var ในคุณสมบัติ)
-
ชื่อสตริง = คุณสมบัติ ชื่อ;
ถ้า (property.PropertyType.IsGenericType &&
property.PropertyType.GetGenericTypeDefinition() == ประเภทของ (EntitySet<>))
-
คุณสมบัติ SetValue (เอนทิตี, โมฆะ, โมฆะ);
-
-
ฟิลด์ System.Reflection.FieldInfo[] = t.GetFields(System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
foreach (ฟิลด์ var ในฟิลด์)
-
ชื่อสตริง = field.Name;
ถ้า (field.FieldType.IsGenericType &&
field.FieldType.GetGenericTypeDefinition() == typeof (EntityRef<>))
-
field.SetValue (เอนทิตี, null);
-
-
System.Reflection.EventInfo eventPropertyChanged = t.GetEvent("คุณสมบัติเปลี่ยน");
System.Reflection.EventInfo eventPropertyChanging = t.GetEvent("PropertyChanging");
ถ้า (eventPropertyChanged != null)
-
eventPropertyChanged.RemoveEventHandler (เอนทิตี, null);
-
ถ้า (eventPropertyChanging != null)
-
eventPropertyChanging.RemoveEventHandler (เอนทิตี, null);
-
-
หลังจากได้รับเอนทิตีแล้ว คุณควรใช้เมธอด Detach(entity) เพื่อแยกเอนทิตีออกจาก DataContext ดั้งเดิม จากนั้นแนบไปกับ DataContext ใหม่