ดังที่เราได้กล่าวไว้ก่อนหน้านี้ การควบคุมที่ผูกกับข้อมูลจะจัดเก็บค่าที่ส่งไปยังแหล่งข้อมูลในพจนานุกรมคีย์อิสระ ค่า (ค่าใหม่) และพจนานุกรม OldValues ตามค่าเริ่มต้น SqlDataSource และ ObjectDataSource จะละเว้นฟิลด์ OldValues และใช้เฉพาะคีย์และค่าเท่านั้น ลักษณะการทำงานนี้ตรวจพบโดยคุณสมบัติ ConflictDetection ของแหล่งข้อมูล ซึ่งตั้งค่าเป็น OverwriteChanges ตามค่าเริ่มต้น รูปแบบ OverwriteChanges หมายความว่า "จับคู่เฉพาะค่าคีย์หลักเพื่ออัปเดตหรือลบบันทึก" การดำเนินการนี้หมายความว่าการอัปเดตหรือการลบเรกคอร์ดไม่ได้คำนึงว่าค่าพื้นฐานของเรกคอร์ดมีการเปลี่ยนแปลงหรือไม่ ภายใต้สถานการณ์ปกติ สถานะในอุดมคติคือการอนุญาตให้การดำเนินการอัปเดตหรือลบสำเร็จเฉพาะเมื่อค่าของแถวข้อมูลตรงกับค่าที่เลือกไว้ตั้งแต่แรกทุกประการ ในสถานการณ์ที่เหมาะสมนี้ หากผู้ใช้รายอื่นอัปเดตแถวระหว่างเวลาที่คุณเลือกและเวลาที่คุณอัปเดต การดำเนินการอัปเดตของคุณจะล้มเหลว แหล่งข้อมูลยังสนับสนุนการดำเนินการนี้ด้วยการตั้งค่าคุณสมบัติ ConflictDetection เป็น CompareAllValues ในโหมดนี้ แหล่งข้อมูลจะใช้ OldValues กับคำสั่งหรือเมธอด และจะใช้ค่าเหล่านี้เพื่อให้แน่ใจว่าการดำเนินการอัปเดตหรือลบจะต้องตรงกับค่าทั้งหมดของบันทึกก่อนที่จะอัปเดตหรือลบ บันทึก. คุณต้องตั้งค่าคุณสมบัติ OldValuesParameterFormatString เป็นสตริงรูปแบบคอมโพเนนต์ .NET Framework ที่ถูกต้อง (เช่น "Original_{0}") เพื่อระบุวิธีเปลี่ยนชื่อพารามิเตอร์ในพจนานุกรม OldValues และ Keys เพื่อแยกความแตกต่างจากพารามิเตอร์ NewValues
ตัวอย่างโค้ดต่อไปนี้แสดงคำสั่ง SQL ทั่วไปที่ใช้โดยตัวควบคุม SqlDataSource ในโหมด OverwriteChanges และ CompareAllValues ฟิลด์ ID จะถือว่าเป็นฟิลด์คีย์หลัก โปรดทราบว่าคำสั่งหลังจะเปรียบเทียบค่าดิบทั้งหมดของแถวข้อมูลในส่วนคำสั่ง WHERE แทนที่จะเป็นเพียงคีย์หลัก ในกรณีนี้ OldValuesParameterFormatString ของแหล่งข้อมูลจำเป็นต้องตั้งค่าเป็น "Original_{0}"
เลือก [ID], [ชื่อ], [ที่อยู่] จาก [ผู้ติดต่อ]
-- เขียนทับการเปลี่ยนแปลง
อัปเดต [ผู้ติดต่อ] ตั้งค่า [ชื่อ] = @ชื่อ, [ที่อยู่] = @ที่อยู่ โดยที่ [ID] = @ID
ลบจาก [ผู้ติดต่อ] โดยที่ [ID] = @ID
-- CompareAllValues
อัปเดต [ผู้ติดต่อ] SET [ชื่อ] = @ชื่อ, [ที่อยู่] = @ที่อยู่ โดยที่ [ID] = @Original_ID
และ [ชื่อ] = @Original_Name และ [ที่อยู่] = @Original_Address
ลบจาก [ผู้ติดต่อ] โดยที่ [ID] = @Original_ID และ [ชื่อ] = @Original_Name
และ [ที่อยู่] = @Original_Address
โปรดทราบว่า OldValues ไม่จำเป็นสำหรับการดำเนินการแทรก และ ConflictDetection มีความหมายสำหรับการดำเนินการอัปเดตและลบเท่านั้น
ตัวอย่างต่อไปนี้แสดงให้เห็นถึงลักษณะการทำงานเมื่อมีข้อขัดแย้งเกิดขึ้น ในการรันตัวอย่างนี้ คุณต้องเปิดสองอินสแตนซ์ของตัวอย่างในหน้าต่างเบราว์เซอร์สองหน้าต่างแยกกัน (คลิก "เรียกใช้ตัวอย่าง" สองครั้ง) จากนั้นคลิกปุ่ม "แก้ไข" บนแถวเดียวกันของทั้งสองแบบฟอร์มเพื่อนำแถวเข้าสู่โหมดแก้ไข เปลี่ยนค่าในหน้าต่างแรกแล้วคลิก "อัปเดต" โปรดทราบว่าการอัปเดตสำเร็จ ในหน้าต่างที่สอง ให้ป้อนค่าใหม่ในแถวแล้วคลิก "อัปเดต" การดำเนินการอัปเดตนี้ไม่สำเร็จเนื่องจากค่าของแถวข้อมูลพื้นฐานมีการเปลี่ยนแปลงไปแล้วในการดำเนินการอัปเดตครั้งแรก ตัวอย่างนี้จะตรวจสอบคุณสมบัติ AffectedRows ของพารามิเตอร์เหตุการณ์ที่อัปเดตหรือถูกลบ ซึ่งเป็น 0 เพื่อยืนยันว่ามีข้อขัดแย้งเกิดขึ้น
<สคริปต์ runat="เซิร์ฟเวอร์">
SqlDataSource1_Updated ย่อยที่ได้รับการป้องกัน (ผู้ส่งเป็นวัตถุ e As SqlDataSourceStatusEventArgs)
ถ้า e.AffectedRows = 0 แล้ว
Response.Write("เปลี่ยนแถว, ยกเลิกการอัปเดต <br />")
สิ้นสุดถ้า
สิ้นสุด Sub
Protected Sub SqlDataSource1_Deleted (ผู้ส่งเป็นวัตถุ e As SqlDataSourceStatusEventArgs)
ถ้า e.AffectedRows = 0 แล้ว
Response.Write("เปลี่ยนแถว, ลบถูกยกเลิก<br />")
สิ้นสุดถ้า
จบหมวดย่อย
</script><
เมื่ออัปเดตหรือลบใช้ UI ที่เป็นเทมเพลต ค่าเก่าของฟิลด์การเชื่อมโยงข้อมูลแบบสองทางโดยใช้ไวยากรณ์การเชื่อมโยงจะยังคงอยู่ สำหรับ Delete หมายความว่าคุณต้องใช้ไวยากรณ์ Bind สำหรับค่าที่ผูกกับข้อมูลใน ItemTemplate จุดประสงค์คือเพื่อรักษาค่าเก่าที่จำเป็นสำหรับการดำเนินการลบ ตัวอย่างต่อไปนี้แสดงให้เห็นถึงเทคนิคนี้
<asp:GridView……>
<คอลัมน์>
<asp:CommandField ShowDeleteButton="True" ShowEditButton="True" />
<asp:TemplateField HeaderText = "ContactID" InsertVisible = "False" SortExpression = "ContactID">
<เทมเพลตรายการ>
<asp:Label ID="Label1" runat="server" Text='<%# Bind("ContactID") %><'></asp:Label>
</เทมเพลตรายการ">
<แก้ไขเทมเพลตรายการ>
<asp:Label ID="Label3" runat="server" Text='<%# Eval("ContactID") %><'></asp:Label>
</แก้ไขเทมเพลตรายการ><
</asp:TemplateField>
<asp:TemplateField HeaderText = "ContactName" SortExpression = "ContactName">
<เทมเพลตรายการ>
<asp:Label ID="Label2" runat="server" Text='<%# Bind("ContactName") %><'></asp:Label>
</เทมเพลตรายการ">
<แก้ไขเทมเพลตรายการ>
<asp:TextBox ID="TextBox1" runat="server" Text='<%# Bind("ContactName") %><'></asp:TextBox>
</แก้ไขเทมเพลตรายการ><
</asp:TemplateField>
</คอลัมน์>
</asp:GridView>
คุณสามารถจัดการข้อผิดพลาดการตรวจจับข้อขัดแย้งได้อย่างนุ่มนวล โดยแจ้งให้ผู้ใช้ทราบว่าข้อมูลพื้นฐานมีการเปลี่ยนแปลง แสดงค่าที่เปลี่ยนแปลงแก่ผู้ใช้ และอนุญาตให้ผู้ใช้เลือกที่จะส่งหรือละทิ้งการดำเนินการของตน ตัวอย่างต่อไปนี้สาธิตวิธีหนึ่งที่เป็นไปได้ในการจัดการการตรวจจับข้อขัดแย้ง โปรดทราบว่าพารามิเตอร์เหตุการณ์ RowUpdated ของ DetailView จะถูกส่งผ่านพจนานุกรมที่สามารถใช้เพื่อตรวจจับค่าที่ผู้ใช้ป้อน คุณยังสามารถตั้งค่าคุณสมบัติ KeepInEditMode ของพารามิเตอร์เหตุการณ์นี้เพื่อให้ DetailView อยู่ในโหมดแก้ไขในขณะที่ผู้ใช้ตัดสินใจว่าจะจัดการกับข้อขัดแย้งอย่างไร ตัวอย่างนี้ทดสอบวิธีการที่คล้ายกันกับวิธีก่อนหน้า โดยเปิดสองหน้าต่างพร้อมกันเพื่อสร้างการอัปเดตที่ขัดแย้งกัน
DetailView1_ItemUpdated ย่อยที่ได้รับการป้องกัน (ผู้ส่ง ByVal เป็นวัตถุ, ByVal e As System.Web.UI.WebControls.DetailsViewUpdatedEventArgs)
ถ้า e.AffectedRows = 0 แล้ว
' ใส่ DetailView ในโหมดแก้ไขและซิงโครไนซ์กับฐานข้อมูล e.KeepInEditMode = True
รายละเอียด View1.DataBind()
' เติม DetailView อีกครั้งด้วยค่าที่ผู้ใช้ป้อน
หรี่เป็นกล่องข้อความ
t = DetailView1.Rows(1).Cells(1).Controls(0)
t.Text = e.NewValues("วันที่สั่งซื้อ")
t = DetailView1.Rows(2).Cells(1).Controls(0)
t.Text = e.NewValues("ShipCountry")
ErrorPanel.Visible = True
อื่น
ErrorPanel.Visible = เท็จ
สิ้นสุดถ้า
สิ้นสุด Sub
Protected Sub DetailView1_ModeChanging (ผู้ส่ง ByVal As Object, ByVal e As System.Web.UI.WebControls.DetailsViewModeEventArgs)
ถ้า e.CancelingEdit = True และ ErrorPanel.Visible = True แล้ว
ErrorPanel.Visible = เท็จ
สิ้นสุดถ้า
สถานการณ์จะคล้ายกันเมื่อ
End Sub
ใช้ ObjectDataSourceโปรดทราบว่าเนื่องจากคุณสมบัติ ConflictDetection ของแหล่งข้อมูลถูกตั้งค่าเป็น CompareAllValues แหล่งข้อมูลจะค้นหาโอเวอร์โหลด UpdateContact ที่ยอมรับค่าดิบของแต่ละฟิลด์ของออบเจ็กต์ผู้ติดต่อ
คุณยังสามารถใช้คุณสมบัติ DataObjectTypeName และ CompareAllValues ร่วมกันได้ ในกรณีนี้ ObjectDataSource จะค้นหาการโอเวอร์โหลด UpdateContact ที่ยอมรับพารามิเตอร์เพียงสองตัวเท่านั้น (ผู้ติดต่อทั้งคู่) พารามิเตอร์แรกคือออบเจ็กต์ Contact ที่เก็บค่าใหม่ และพารามิเตอร์ตัวที่สองคือออบเจ็กต์ Contact ที่เก็บค่าเก่า