การกำหนดค่าประเภทคอลัมน์ การใช้เทคโนโลยี Pass-Through โดยตรงผ่านฟังก์ชัน SQLEXEC()

คุณได้เห็นแบบแผนการกำหนดค่าบางอย่างที่แนวทางนี้ใช้แล้ว โดยเฉพาะอย่างยิ่ง คุณได้เห็นวิธีปรับแต่งโมเดลเอนทิตีโดยใช้คำอธิบายประกอบข้อมูลที่แสดงผ่านแอตทริบิวต์ C# และการใช้ Fluent API ในบทความนี้และบทความต่อๆ ไป เราจะมาดูวิธีการปรับแต่งโมเดลข้อมูลของคุณอย่างละเอียดยิ่งขึ้น

การตั้งค่าประเภทคอลัมน์

ก่อนหน้านี้ในกรณีการใช้งาน Code-First คุณได้เห็นการใช้แอตทริบิวต์ข้อมูลเมตาบางอย่างที่อนุญาตให้คุณปรับแต่งประเภทข้อมูลของคอลัมน์ในตารางฐานข้อมูลและใช้ข้อจำกัดกับคอลัมน์นั้นได้ (เช่น การระบุว่ารองรับค่า NULL หรือไม่) เราจะดูรายละเอียดคุณลักษณะเหล่านี้โดยละเอียดต่อไป

การจำกัดความยาว

ตารางด้านล่างแสดงแบบแผนในการจำกัดความยาวของคอลัมน์ การใช้งานเป็นคำอธิบายประกอบและใน Fluent API:

การจำกัดความยาวสามารถกำหนดให้กับสตริงหรืออาร์เรย์ไบต์ได้ ตามแบบแผน Code-First จะใช้ข้อจำกัดความยาวสูงสุดเท่านั้น ซึ่งหมายความว่า SQL Server จะตั้งค่าชนิดข้อมูลสำหรับสตริงและอาร์เรย์ไบต์เป็น NVARCHAR(n) และ VARBINARY(n) โดยที่ n คือความยาวที่ระบุในข้อจำกัด ตามค่าเริ่มต้น ถ้าไม่มีการจำกัดความยาวกับคุณสมบัติของโมเดล Code-First จะตั้งค่าความยาวคอลัมน์สูงสุดที่เป็นไปได้เป็น NVARCHAR(สูงสุด) และ VARBINARY(สูงสุด)

ดังที่แสดงในตาราง การใช้คำอธิบายประกอบข้อมูล คุณสามารถตั้งค่าความยาวขั้นต่ำสำหรับคุณสมบัติของโมเดลได้โดยใช้แอตทริบิวต์ MinLength - ข้อจำกัดนี้ไม่มีผลกับตารางฐานข้อมูล (ดังที่อธิบายไว้ก่อนหน้านี้ คุณลักษณะของข้อมูลเมตาสามารถใช้ได้ไม่เพียงแต่ใน Entity Framework เท่านั้น แต่ยังสามารถใช้ในการตรวจสอบโมเดล ASP.NET ได้ด้วย) นี่คือสาเหตุที่ Fluent API ไม่มีเมธอด HasMinLength() เนื่องจาก API นี้เป็นส่วนหนึ่งของ Entity Framework และมีหน้าที่รับผิดชอบในการตั้งค่าแบบแผนที่เกี่ยวข้องกับ Code-First เท่านั้น

เป็นที่น่าสังเกตว่าคุณสามารถระบุความยาวฟิลด์สูงสุดและต่ำสุดในแอตทริบิวต์ StringLength เดียวได้ โดยใช้พารามิเตอร์ที่มีชื่อของแอตทริบิวต์นี้ ตัวอย่างต่อไปนี้แสดงการใช้ข้อจำกัดด้านความยาวโดยใช้คำอธิบายประกอบ (ในที่นี้เราใช้โมเดลตัวอย่างที่เราสร้างขึ้นในบทความ “การใช้โค้ดเป็นอันดับแรก” ก่อนหน้านี้):

การใช้ระบบ; ใช้ System.Collections.Generic; ใช้ System.ComponentModel.DataAnnotations; เนมสเปซ CodeFirst ( ลูกค้าคลาสสาธารณะ ( public int CustomerId ( get; set; ) สตริงสาธารณะ ชื่อ ( get; set; ) สตริงสาธารณะ นามสกุล ( get; set; ) สตริงสาธารณะ อีเมล ( get; set; ) public int Age ( get; set ; ) รูปภาพไบต์สาธารณะ (get; set; ) // ลิงก์ไปยังคำสั่งรายการเสมือนสาธารณะ คำสั่งซื้อ ( get; set; ) ) คลาสสาธารณะ คำสั่งซื้อ ( public int OrderId ( get; set; ) สตริงสาธารณะ ชื่อผลิตภัณฑ์ ( get; set; ) สตริงสาธารณะ คำอธิบาย ( get; set; ) public int จำนวน ( get; set; ) public DateTime PurchaseDate ( get; set; ) // ลิงก์ไปยังผู้ซื้อ ลูกค้าลูกค้าสาธารณะ ( get; set; ) ) )

และในโค้ดต่อไปนี้ การตั้งค่าที่คล้ายกันเกิดขึ้นโดยใช้ Fluent API (ฉันขอเตือนคุณว่าหากต้องการใช้ API นี้ คุณจะต้องแทนที่วิธีการกำหนดค่า OnModelCreating()ในคลาสบริบท ซึ่งในตัวอย่างของเราคือคลาส SampleContext):

().คุณสมบัติ(c => c.FirstName).HasMaxLength(30); modelBuilder.Entity ().คุณสมบัติ(c => c.อีเมล).HasMaxLength(100); modelBuilder.Entity ().คุณสมบัติ(o => o.ชื่อผลิตภัณฑ์).HasMaxLength(500); -

การระบุประเภทคอลัมน์อย่างชัดเจน

ตามที่อธิบายไว้ก่อนหน้านี้ Entity Framework จะแมปประเภทข้อมูลแบบจำลองกับประเภทข้อมูลที่เข้ากันได้กับ SQL โดยอัตโนมัติ Code-First ช่วยให้คุณควบคุมกระบวนการนี้โดยการระบุประเภทข้อมูลสำหรับคอลัมน์อย่างชัดเจน ดังที่แสดงในตัวอย่างด้านล่าง:

ลูกค้าคลาสสาธารณะ ( public int CustomerId ( get; set; ) // ... ไบต์สาธารณะ Photo ( get; set; ) // ... ) // เช่นเดียวกับ Fluent API การแทนที่การป้องกันจะแทนที่เป็นโมฆะ OnModelCreating(DbModelBuilder modelBuilder) ( modelBuilder. เอนทิตี ().Property(c => c.CustomerId) .HasColumnType("smallint"); modelBuilder.Entity ().Property(c => c.Photo) .HasColumnType("รูปภาพ"); -

รองรับคอลัมน์ NULL

แบบแผน Entity Framework สำหรับรองรับค่า Null ในคอลัมน์ตารางคือประเภท .NET ทั้งหมดที่สนับสนุน null (อ็อบเจ็กต์) จะแมปกับประเภท SQL ด้วยคำสั่งย่อย NULL ที่ชัดเจน และในทางกลับกันสำหรับประเภท .NET ที่ไม่รองรับโครงสร้าง null ( ) ถูกแมปกับประเภท SQL โดยการระบุคำสั่ง NOT NULL อย่างชัดเจน

หากต้องการระบุอย่างชัดเจนว่าประเภทข้อมูลไม่ควรรองรับค่า NULL คุณต้องใช้แอตทริบิวต์ Required ในโมเดลข้อมูล หรือใช้เมธอด IsRequired() ของออบเจ็กต์การกำหนดค่าใน Fluent API เพื่อระบุอย่างชัดเจนว่าชนิดข้อมูลต้องสนับสนุนค่า NULL คุณจำเป็นต้องใช้คอลเลกชัน Nullable หรือใช้ไวยากรณ์ C# ซึ่งสำหรับประเภทค่าที่รองรับ null จะมีเครื่องหมายคำถามอยู่หลังประเภท (เช่น int?)

ตัวอย่างด้านล่างแสดงการใช้การตั้งค่าเหล่านี้เพื่อกำหนดค่าข้อมูลเกี่ยวกับประเภทที่เป็นโมฆะหรือไม่เป็นโมฆะในตารางฐานข้อมูล:

ลูกค้าคลาสสาธารณะ ( public int CustomerId ( get; set; ) สตริงสาธารณะ FirstName ( get; set; ) สตริงสาธารณะ LastName ( get; set; ) // ฟิลด์นี้สามารถเป็น NULL // เพราะเราได้ระบุประเภท int? public ไว้อย่างชัดเจน int? Age ( get; set; ) // คล้ายกับคุณสมบัติก่อนหน้า nullable Age1 ( get; set; ) // ... ) // เหมือนกับ Fluent API protected override void OnModelCreating(DbModelBuilder modelBuilder) ( modelBuilder.Entity ().คุณสมบัติ(c => c.FirstName).IsRequired(); modelBuilder.Entity ().ทรัพย์สิน(c => c.นามสกุล).จำเป็น(); -

โปรดทราบว่า Fluent API สามารถกำหนดค่าให้รองรับ NOT NULL สำหรับประเภทข้อมูลอ้างอิงเท่านั้น และไม่สามารถกำหนดค่าให้รองรับ NULL สำหรับประเภทค่าได้เนื่องจาก การสนับสนุน NULL สำหรับสิ่งเหล่านั้นจะถูกระบุอย่างชัดเจนเมื่อประกาศประเภทคุณสมบัติในคลาสโมเดล

การตั้งค่าคีย์หลัก

Entity Framework กำหนดให้แต่ละคลาสโมเดลเอนทิตีมีคีย์เฉพาะ (เนื่องจากแต่ละตารางในฐานข้อมูลเชิงสัมพันธ์ต้องมีคีย์หลัก) คีย์นี้ใช้ในวัตถุบริบทเพื่อติดตามการเปลี่ยนแปลงของวัตถุแบบจำลอง Code-First ตั้งสมมติฐานบางอย่างเมื่อค้นหาคีย์ในตาราง ตัวอย่างเช่น เมื่อเราสร้างฐานข้อมูลสำหรับคลาสเอนทิตีลูกค้าและคำสั่งซื้อก่อนหน้านี้ในแนวทาง Code-First Entity Framework จะทำเครื่องหมายฟิลด์ CustomerId และ OrderId ในตารางเป็นคีย์หลัก และตั้งค่าให้รองรับประเภทที่ไม่สามารถเป็นค่าว่างได้:

EF ยังเพิ่มการรองรับการเพิ่มอัตโนมัติในฟิลด์เหล่านี้โดยอัตโนมัติ (ฉันขอเตือนคุณว่าใน T-SQL ทำได้โดยใช้คำสั่ง IDENTITY) โดยส่วนใหญ่ คีย์หลักในฐานข้อมูลจะเป็นประเภท INT หรือ GUID แม้ว่าประเภทดั้งเดิมใดๆ จะสามารถใช้เป็นคีย์หลักได้ก็ตาม คีย์หลักในฐานข้อมูลสามารถประกอบด้วยคอลัมน์ตารางได้หลายคอลัมน์ ในทำนองเดียวกัน คีย์โมเดลเอนทิตี EF สามารถประกอบด้วยคุณสมบัติโมเดลได้หลายรายการ ต่อมาคุณจะเห็นวิธีตั้งค่าคีย์ผสม

การตั้งค่าคีย์หลักอย่างชัดเจน

ในกรณีของสองคลาสลูกค้าและคำสั่งซื้อของเรา ไม่จำเป็นต้องกังวลเกี่ยวกับการระบุคีย์หลักอย่างชัดเจนเพราะว่า เราใช้คุณสมบัติ CustomerId และ OrderId ซึ่งเป็นไปตามหลักการตั้งชื่อคีย์ - “+Id” ลองดูตัวอย่างที่คุณต้องระบุคีย์หลักอย่างชัดเจน เพิ่มคลาสแบบง่ายต่อไปนี้ให้กับไฟล์โมเดลของคุณ:

โครงการระดับสาธารณะ ( ตัวระบุ Guid สาธารณะ ( get; set; ) DateTime StartDate สาธารณะ ( get; set; ) DateTime EndDate สาธารณะ ( get; set; ) ต้นทุนทศนิยมสาธารณะ ( get; set; ) )

เป้าหมายของเราคือการระบุว่าคุณสมบัติ Identifier ในคลาสนี้เป็นคีย์หลักของตาราง อย่างที่คุณเห็น ชื่อของคุณสมบัตินี้ไม่เป็นไปตามหลักการตั้งชื่อ Entity Framework สำหรับคีย์หลัก

เพิ่มคำอธิบายของตารางใหม่ให้กับคลาสบริบท SampleContext:

คลาสสาธารณะ SampleContext: DbContext ( // ... สาธารณะ DbSet โครงการ (get; set; ) // ... )

ตอนนี้ หากคุณเรียกใช้แอปพลิเคชันของเราและแทรกข้อมูลของลูกค้าใหม่ ข้อยกเว้นจะถูกสร้างขึ้นในคลาส DbModelBuilder เนื่องจากไม่สามารถสร้างแบบจำลองข้อมูลเอนทิตีได้อย่างถูกต้อง (ฉันขอเตือนคุณว่าแอปพลิเคชันของเราเป็นไซต์ ASP.NET ธรรมดาซึ่งมีการนำความสามารถในการแทรกลูกค้าใหม่มาใช้)

ข้อยกเว้นนี้มีสาเหตุมาจาก Entity Framework ไม่พบคุณสมบัติชื่อ Id หรือ ProjectId ในตาราง Project และ Code-First ไม่สามารถระบุได้ว่าจะใช้เขตข้อมูลใดเป็นคีย์หลักของตาราง ปัญหานี้สามารถแก้ไขได้โดยใช้คำอธิบายประกอบข้อมูลหรือ Fluent API ดังที่แสดงในตัวอย่างด้านล่าง:

โครงการคลาสสาธารณะ ( ตัวระบุ Guid สาธารณะ ( get; set; ) DateTime StartDate สาธารณะ ( get; set; ) DateTime EndDate สาธารณะ (get; set; ) ต้นทุนทศนิยมสาธารณะ (get; set; ) ) // เหมือนกับ Fluent API ที่ป้องกันการแทนที่ แทนที่เป็นโมฆะ OnModelCreating(DbModelBuilder modelBuilder) ( modelBuilder.Entity ().HasKey(p => p.Identifier); -

โปรดทราบว่าเมื่อใช้ Fluent API จะมีการระบุเมธอด HasKey() หลังจากการเรียกเมธอด Entity () ไม่ใช่หลังจากเรียกเอนทิตี ().Property() ดังตัวอย่างข้างต้น เนื่องจาก คีย์หลักถูกตั้งค่าไว้ที่ระดับตาราง ไม่ใช่ที่ระดับคุณสมบัติ

การตั้งค่าการเพิ่มอัตโนมัติสำหรับคีย์หลัก

ดังที่เห็นได้จากตาราง ตามแบบแผน Entity Framework ระบุการเพิ่มอัตโนมัติสำหรับคุณสมบัติประเภท int ในตารางโปรเจ็กต์ที่สร้างขึ้นก่อนหน้านี้ ประเภทคีย์หลักจะถูกระบุเป็น Guid ซึ่งส่งผลให้ EF ไม่ใช้ตัวนับสำหรับฟิลด์นี้เมื่อสร้างตารางในฐานข้อมูล นี่แสดงในรูป:

มาเพิ่มเว็บฟอร์มใหม่ในโครงการของเรา ซึ่งเราจะเรียกว่า DatabaseGenerated.aspx ในตัวจัดการ Page_Load ให้เพิ่มโค้ดต่อไปนี้โดยที่เราเพิ่มข้อมูลใหม่ลงในตารางโครงการ ในกรณีนี้ ข้อมูลนี้จะถูกเพิ่มทุกครั้งที่เราเปิดหน้าแบบฟอร์มบนเว็บในเบราว์เซอร์

การใช้ระบบ; โดยใช้ System.Data.Entity; ใช้ CodeFirst; namespace ProfessorWeb.EntityFramework ( คลาสสาธารณะบางส่วน DatabaseGenerated: System.Web.UI.Page ( protected void Page_Load(object sender, EventArgs e) ( // การตั้งค่านี้จำเป็นเพื่อให้ฐานข้อมูลถูกลบโดยอัตโนมัติ // ถูกลบและสร้างใหม่เมื่อ โครงสร้างเปลี่ยนโมเดล // (เพื่อให้สะดวกในการทดสอบตัวอย่าง) Database.SetInitializer(new DropCreateDatabaseIfModelChanges -

บริบท SampleContext = ใหม่ SampleContext(); โครงการ โครงการ = โครงการใหม่ ( StartDate = DateTime.Now, EndDate = DateTime.Now.AddMonths(1), ต้นทุน = 8000M ); บริบทโครงการเพิ่ม (โครงการ); บริบท บันทึกการเปลี่ยนแปลง (); -

เรียกใช้โครงการ และเปิดเว็บฟอร์ม DatabaseGenerated.aspx เป็นผลให้บันทึกใหม่จะถูกเพิ่มลงในตารางโครงการ:

ด้วยเหตุนี้ เพื่อแก้ไขปัญหานี้ เราจะต้องสร้าง Guid ที่ไม่ซ้ำใครในโค้ด ตารางที่ใช้การเพิ่มอัตโนมัติสำหรับคีย์หลักไม่มีงานพิเศษนี้เพราะว่า สำหรับการแทรกระเบียนใหม่แต่ละรายการ ตัวนับจะสร้างค่าใหม่สำหรับคีย์หลักโดยการดึงค่าคีย์หลักสำหรับระเบียนสุดท้ายและเพิ่ม 1 เข้าไป (หากใช้โครงสร้าง IDENTITY(1,1))

ในการแก้ปัญหานี้สำหรับคีย์ประเภทอื่นที่ไม่ใช่ int คุณต้องใช้แอตทริบิวต์ข้อมูลเมตา DatabaseGenerated ในตัวสร้างที่คุณระบุ การแจงนับ DatabaseGeneratedOptionซึ่งมีสามค่าที่เป็นไปได้:

ไม่มี

ฐานข้อมูลไม่ได้สร้างค่าเฉพาะใดๆ สำหรับคีย์หลัก ที่จริงแล้ว ด้วยตัวเลือกนี้ คุณสามารถปิดการใช้งานการเพิ่มอัตโนมัติให้กับคีย์หลักประเภท int ได้

ตัวตน

เมื่อคุณแทรกค่าลงในตาราง ฐานข้อมูลจะสร้างค่าเฉพาะให้กับคีย์หลัก

คำนวณแล้ว

คล้ายกับ Identity โดยมีข้อยกเว้นเพียงอย่างเดียวว่าคีย์หลักจะถูกสร้างขึ้นไม่เพียงแต่เมื่อแทรกบันทึกลงในตาราง แต่ยังรวมถึงเมื่ออัปเดตด้วย

แก้ไขคลาสโมเดลเพื่อสั่งให้ฐานข้อมูลสร้างคีย์หลักเฉพาะ:

โครงการระดับสาธารณะ ( ตัวระบุ Guid สาธารณะ ( get; set; ) DateTime StartDate สาธารณะ ( get; set; ) DateTime EndDate สาธารณะ ( get; set; ) ต้นทุนทศนิยมสาธารณะ ( get; set; ) )

รันโปรเจ็กต์ตัวอย่างและรีเฟรชเพจสองสามครั้งเพื่อแทรกเรกคอร์ดบางส่วนลงในตาราง และตรวจสอบให้แน่ใจว่าไม่มีข้อยกเว้นอีกต่อไป รูปภาพด้านล่างแสดงข้อมูลที่เพิ่มลงในตาราง:

โปรดทราบรหัสที่สร้างขึ้นโดยอัตโนมัติสำหรับโครงการ ผลลัพธ์เดียวกันนี้สามารถทำได้โดยใช้วิธี HasDatabaseGeneratedOption() ใน Fluent API:

การแทนที่ที่ได้รับการปกป้องถือเป็นโมฆะ OnModelCreating (DbModelBuilder modelBuilder) ( modelBuilder.Entity ().คุณสมบัติ(p => p.Identifier).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity); -

การทำงานกับชนิดข้อมูลที่ซับซ้อน

Entity Framework รองรับความสามารถในการใช้ประเภทที่ซับซ้อนตั้งแต่เวอร์ชัน 1 ที่จริงแล้ว ประเภทที่ซับซ้อนใน .NET นั้นเป็นคลาสที่สามารถอ้างอิงได้ในคลาสโมเดล ประเภทที่ซับซ้อนไม่มีคีย์และสามารถใช้ได้กับออบเจ็กต์โมเดลหลายรายการ ลองดูตัวอย่างโมเดลต่อไปนี้:

ผู้ใช้คลาสสาธารณะ ( public int UserId ( get; set; ) public int SocialNumber ( get; set; ) สตริงสาธารณะ ชื่อ ( get; set; ) สตริงสาธารณะ นามสกุล ( get; set; ) สตริงสาธารณะ StreetAddress ( get; set; ) สาธารณะ string City ( get; set; ) สตริงสาธารณะ ZipCode ( get; set; ) )

ในชั้นเรียนนี้ ที่อยู่ที่อยู่อาศัยของผู้ใช้สามารถแยกออกเป็นชั้นเรียนแยกต่างหากและอ้างอิงถึง:

ผู้ใช้คลาสสาธารณะ ( public int UserId ( get; set; ) public int SocialNumber ( get; set; ) สตริงสาธารณะ ชื่อ ( get; set; ) สตริงสาธารณะ นามสกุล ( get; set; ) ที่อยู่สาธารณะ ที่อยู่ ( get; set; ) ) ที่อยู่คลาสสาธารณะ ( public int AddressId ( get; set; ) สตริงสาธารณะ StreetAddress ( get; set; ) สตริงสาธารณะ เมือง ( get; set; ) สตริงสาธารณะ ZipCode ( get; set; ) )

ตามแบบแผน Entity Framework จะแยกวิเคราะห์โมเดลนี้เป็นสองตารางแยกกัน แต่เป้าหมายของเราคือการสร้างประเภทที่ซับซ้อนจากคลาส Address วิธีดั้งเดิมในการสร้างประเภทที่ซับซ้อนจากคลาส Address คือการลบ AddressId:

ที่อยู่คลาสสาธารณะ ( // public int AddressId ( get; set; ) สตริงสาธารณะ StreetAddress ( get; set; ) สตริงสาธารณะ เมือง ( get; set; ) สตริงสาธารณะ ZipCode ( get; set; ) )

นอกเหนือจากกฎที่ว่าประเภทที่ซับซ้อนจะต้องไม่มีคีย์แล้ว Code-First ยังกำหนดกฎอีกสองข้อที่ต้องปฏิบัติตามเพื่อตรวจจับประเภทที่ซับซ้อน ขั้นแรก ประเภทที่ซับซ้อนจะต้องมีคุณสมบัติอย่างง่ายเท่านั้น ประการที่สอง คลาสที่ใช้ประเภทนี้ไม่ได้รับอนุญาตให้ระบุประเภทคอลเลกชันสำหรับคุณสมบัติของประเภทที่ซับซ้อน กล่าวอีกนัยหนึ่ง หากคุณต้องการใช้ Address ประเภทที่ซับซ้อนในคลาส User คุณสมบัติที่มีประเภทนั้นไม่ควรถูกทำเครื่องหมาย List

หรือใช้คอลเลกชันอื่น

ดังแสดงในรูปด้านล่าง หลังจากรันแอปพลิเคชัน Code-First จะจดจำประเภทที่ซับซ้อนและสร้างฟิลด์พิเศษในตาราง User (อย่าลืมเพิ่มการประกาศ User ในคลาสบริบท):

โปรดสังเกตว่าฟิลด์ที่อธิบายที่อยู่ของผู้ใช้มีชื่อว่า ComplexTypeName_PropertyName อย่างไร นี่คือแบบแผนของ Entity Framework สำหรับการตั้งชื่อประเภทที่ซับซ้อน

การกำหนดค่าประเภทที่ซับซ้อนเพื่อหลีกเลี่ยงการใช้รหัสแบบแรก

จะเกิดอะไรขึ้นถ้าคลาสของคุณที่อธิบายประเภทที่ซับซ้อนไม่เป็นไปตามแบบแผนของ Entity Framework เช่น คุณต้องการใช้ฟิลด์ AddressId ในคลาส Address? หากตอนนี้เราเพิ่มฟิลด์นี้ในคลาส Address และรันโปรเจ็กต์ แทนที่จะเพิ่มตารางผู้ใช้หนึ่งตารางและประเภทที่อยู่ที่ซับซ้อน Entity Framework จะสร้างตารางสองตารางที่เชื่อมโยงถึงกันด้วยคีย์นอก เพื่อแก้ไขปัญหานี้คุณสามารถระบุได้อย่างชัดเจน แอตทริบิวต์ ComplexTypeในคลาสโมเดลหรือการใช้งาน ComplexType() วิธีการคลาส DbModelBuilder ใน Fluent API:

ที่อยู่คลาสสาธารณะ ( public int AddressId ( get; set; ) สตริงสาธารณะ StreetAddress ( get; set; ) สตริงสาธารณะ เมือง ( get; set; ) สตริงสาธารณะ ZipCode ( get; set; ) ) // เหมือนกับ Fluent API ที่ป้องกันการแทนที่ แทนที่เป็นโมฆะ OnModelCreating(DbModelBuilder modelBuilder) ( modelBuilder.ComplexType

(); }

กล่าวไว้ข้างต้นว่าคลาสที่อธิบายประเภทที่ซับซ้อนควรมีคุณสมบัติอย่างง่ายเท่านั้น (นั่นคือ คลาสที่ไม่ได้อ้างถึงอ็อบเจ็กต์อื่น) ข้อตกลงนี้สามารถเอาชนะได้โดยใช้วิธีเดียวกัน ด้านล่างนี้เป็นตัวอย่างที่มีการเพิ่ม UserInfo ประเภทที่ซับซ้อนใหม่ที่อ้างอิงถึงประเภท FullName อื่น:

ผู้ใช้คลาสสาธารณะ ( public int UserId ( get; set; ) public UserInfo UserInfo ( get; set; ) ที่อยู่สาธารณะ ที่อยู่ ( get; set; ) ) UserInfo ระดับสาธารณะ ( public int SocialNumber ( get; set; ) // ไม่ใช่คุณสมบัติแบบง่าย ชื่อเต็มสาธารณะ ชื่อเต็ม ( get; set; ) ) ชื่อเต็มคลาสสาธารณะ ( สตริงสาธารณะ ชื่อ ( get; set; ) สตริงสาธารณะ นามสกุล ( get; set; ) ) ที่อยู่คลาสสาธารณะ ( public int AddressId ( get; set; ) สตริงสาธารณะ StreetAddress ( get; set; ) เมืองสตริงสาธารณะ ( get; set; ) รหัสไปรษณีย์สาธารณะ ( get; set; )

ด้วยการระบุ Code-First ว่า UserInfo เป็นประเภทที่ซับซ้อนโดยใช้แอตทริบิวต์ ComplexType เราจะเอาชนะข้อจำกัดที่กำหนดให้กับประเภทที่ซับซ้อนโดยใช้แบบแผนเริ่มต้น

เป็นที่น่าสังเกตว่า Code-First ช่วยให้คุณปรับแต่งประเภทที่ซับซ้อนได้เหมือนกับตารางทั่วไปโดยใช้ Fluent API หรือคำอธิบายประกอบ ด้านล่างนี้เป็นตัวอย่างสำหรับการตั้งค่าประเภทที่อยู่ที่ซับซ้อน:

ที่อยู่คลาสสาธารณะ ( public int AddressId ( get; set; ) สตริงสาธารณะ StreetAddress ( get; set; ) สตริงสาธารณะ เมือง ( get; set; ) สตริงสาธารณะ ZipCode ( get; set; ) ) // เหมือนกับ Fluent API ที่ป้องกันการแทนที่ แทนที่เป็นโมฆะ OnModelCreating(DbModelBuilder modelBuilder) ( modelBuilder.ComplexType

().ทรัพย์สิน(a => a.ที่อยู่ถนน).HasMaxLength(100); -

รูปด้านล่างแสดงโครงสร้างของตาราง User ที่นี่คุณจะเห็นวิธีที่ EF ตั้งชื่อคุณสมบัติของประเภทที่ซับซ้อนพร้อมการอ้างอิงภายใน และ EF วางข้อจำกัดในฟิลด์ StreetAddress:

คำอธิบายการตั้งค่าอื่นๆ

ในส่วนนี้ เราจะดูการตั้งค่าคอลัมน์ตารางที่เหลือทั้งหมดโดยย่อ ซึ่งไม่ค่อยได้ใช้เนื่องจากคุณลักษณะเฉพาะของการตั้งค่าเหล่านั้น

คอลัมน์การประทับเวลา

ชนิดข้อมูล T-SQL TIMESTAMP ระบุคอลัมน์ที่กำหนดเป็น VARBINARY(8) หรือ BINARY(8) ขึ้นอยู่กับความเป็นโมฆะของคอลัมน์ สำหรับแต่ละฐานข้อมูล ระบบจะรักษาตัวนับที่เพิ่มขึ้นในแต่ละแถวที่มีเซลล์ TIMESTAMP ถูกแทรกหรืออัพเดต และกำหนดค่านั้นให้กับเซลล์นั้น ดังนั้นเมื่อใช้เซลล์ TIMESTAMP คุณสามารถกำหนดเวลาสัมพัทธ์ของการแก้ไขแถวตารางที่เกี่ยวข้องครั้งล่าสุดได้ (ROWVERSION เป็นคำพ้องสำหรับ TIMESTAMP)

โดยตัวมันเอง ค่าที่จัดเก็บไว้ในคอลัมน์ TIMESTAMP นั้นไม่สำคัญ โดยทั่วไปคอลัมน์นี้ใช้เพื่อพิจารณาว่าแถวของตารางใดแถวหนึ่งมีการเปลี่ยนแปลงนับตั้งแต่มีการเข้าถึงครั้งล่าสุดหรือไม่ ซึ่งช่วยให้สามารถเข้าถึงตารางฐานข้อมูลพร้อมกันได้โดยอนุญาตให้เธรดอื่นบล็อกหากเธรดปัจจุบันเปลี่ยนค่าในแถว

ใน Code-First เพื่อระบุว่าคอลัมน์ควรเป็นประเภท TIMESTAMP ต้องใช้ชื่อเดียวกัน แอตทริบิวต์การประทับเวลาในคำอธิบายประกอบหรือ วิธีการ IsRowVersion()ใน Fluent API ดังแสดงในตัวอย่างด้านล่าง:

ไบต์สาธารณะ RowVersion (get; set; ) // เหมือนกับ Fluent API protected override void OnModelCreating(DbModelBuilder modelBuilder) ( modelBuilder.Entity ().คุณสมบัติ(p => p.RowVersion).IsRowVersion(); -

วิธีที่ใช้กันทั่วไปน้อยกว่าในการรับรองความปลอดภัยเมื่อทำงานกับเธรดที่ทำงานพร้อมกันคือ การระบุการตรวจสอบการทำงานพร้อมกันสำหรับแต่ละคอลัมน์ วิธีนี้สามารถใช้ใน DBMS ที่ไม่รองรับประเภท Timestamp/Rowversion เมื่อดำเนินการในลักษณะนี้ เธรดจะไม่ตรวจสอบว่าเรกคอร์ดในตารางมีการเปลี่ยนแปลงหรือไม่ แต่เพียงบล็อกการเข้าถึงเธรดอื่นจนกว่ากระบวนการเขียนจะเสร็จสิ้น หากต้องการระบุคอลัมน์ที่ควรผ่านการตรวจสอบพร้อมกัน ให้ใช้ แอตทริบิวต์ ConcurrencyCheckในคำอธิบายประกอบหรือ วิธีการ IsConcurrencyToken()ใน Fluent API

การเปลี่ยนการเข้ารหัสสตริงจาก Unicode เป็น ASCII

ตามค่าเริ่มต้น Entity Framework จะแปลงชนิดข้อมูลสตริงโมเดลทั้งหมด เช่น สตริงหรืออักขระ เป็นชนิดข้อมูลสตริง SQL ที่ใช้การเข้ารหัส Unicode แบบสองไบต์ NVARCHAR หรือ NCHAR คุณสามารถเปลี่ยนลักษณะการทำงานนี้และแจ้งให้ EF ใช้การเข้ารหัส ASCII ไบต์เดี่ยวได้ - ประเภท VARCHAR และ CHAR จะถูกนำมาใช้ตามนั้น ในการทำเช่นนี้คุณต้องใช้ วิธีการ IsUnicode()โดยที่พารามิเตอร์บูลีนส่งผ่านไปเป็นเท็จใน Fluent API คำอธิบายประกอบไม่ได้จัดเตรียมความสามารถในการปรับแต่งการเข้ารหัสสตริง

การระบุความแม่นยำสำหรับประเภททศนิยม

หากต้องการระบุความแม่นยำสำหรับประเภททศนิยม (จำนวนหลักในตัวเลข) และมาตราส่วน (จำนวนหลักทางด้านขวาของจุดทศนิยมในตัวเลข) คุณสามารถใช้ วิธีการ HasPrecision()โดยส่งพารามิเตอร์สองตัวไปให้ ซึ่งใช้ใน Fluent API คำอธิบายประกอบข้อมูลใน Code-First ไม่มีทางเลือกอื่นสำหรับวิธีนี้ ตามค่าเริ่มต้น Entity Framework จะตั้งค่าความแม่นยำเป็น 18 และมาตราส่วนเป็น 2 สำหรับประเภททศนิยม

ตัวอย่างด้านล่างแสดงการใช้วิธีนี้สำหรับคุณสมบัติต้นทุนของตารางโครงการที่เราสร้างไว้ก่อนหน้านี้ เมื่อดูที่คีย์หลัก:

การแทนที่ที่ได้รับการปกป้องถือเป็นโมฆะ OnModelCreating (DbModelBuilder modelBuilder) ( modelBuilder.Entity ().คุณสมบัติ(p => p.Cost).มีความแม่นยำ(6, 3); -

บ่งชี้ว่าคอลัมน์ใหม่เป็นคอลัมน์ข้อมูลประจำตัว เมื่อมีการเพิ่มแถวใหม่ลงในตาราง โปรแกรมฐานข้อมูลจะสร้างค่าลำดับเฉพาะสำหรับคอลัมน์นั้น โดยทั่วไปคอลัมน์ตัวระบุจะใช้กับข้อจำกัดคีย์หลักเพื่อรักษาเอกลักษณ์ของตัวระบุแถวในตาราง คุณสมบัติ IDENTITY สามารถกำหนดให้กับคอลัมน์ประเภท Tinyint, Smallint, Int, Bigint, Decimal(p,0) หรือ numeric(p,0) คุณสามารถสร้างคอลัมน์ข้อมูลประจำตัวได้เพียงคอลัมน์เดียวต่อตาราง ไม่สามารถใช้ค่าเริ่มต้นในคอลัมน์ข้อมูลประจำตัวได้ คุณต้องระบุทั้งค่าเริ่มต้นและส่วนเพิ่ม มิเช่นนั้น คุณจะระบุอะไรไม่ได้เลย หากไม่ได้ระบุค่าใดเลย ค่าเริ่มต้นคือ (1,1)

ไวยากรณ์:

[ ตัวตน [ (เมล็ด, การเพิ่มขึ้น) ]

อี๊ด-ค่าที่ใช้สำหรับแถวแรกที่โหลดลงในตาราง

ฉันเพิ่มขึ้น - สค่าที่เพิ่มขึ้นที่เพิ่มให้กับค่า ID ของแถวที่โหลดก่อนหน้านี้

[ บัตรลงเวลา ตัวเลข] INT IDENTITY (1, 1) ไม่ใช่คีย์หลักที่เป็นโมฆะ

    เขตข้อมูลจากการคำนวณ

ไวยากรณ์:

<имя_столбца> เช่น <выражение> ]

นิพจน์ที่ระบุค่าของคอลัมน์จากการคำนวณ คอลัมน์จากการคำนวณคือคอลัมน์เสมือนที่ไม่ได้จัดเก็บไว้ในตาราง เว้นแต่จะยังคงอยู่ ค่าของคอลัมน์จะคำนวณตามนิพจน์ที่ใช้คอลัมน์อื่นในตารางเดียวกัน

ตัวอย่างเช่น คำจำกัดความของคอลัมน์จากการคำนวณอาจเป็น:

ต้นทุนของสินค้าเช่น ราคาสำหรับหนึ่ง * ปริมาณ.

นิพจน์อาจเป็นชื่อคอลัมน์ที่ไม่ได้คำนวณ ค่าคงที่ ฟังก์ชัน ตัวแปร หรือชุดค่าผสมใดๆ ของสิ่งเหล่านี้ ซึ่งเชื่อมต่อกันด้วยตัวดำเนินการตั้งแต่ 1 ตัวขึ้นไป นิพจน์ไม่สามารถเป็นแบบสอบถามย่อยหรือมีนามแฝงประเภทข้อมูลได้

คอลัมน์จากการคำนวณสามารถใช้ในรายการที่เลือก ส่วนคำสั่ง WHERE ส่วนคำสั่ง ORDER BY และที่อื่นๆ ที่สามารถใช้นิพจน์ทั่วไปได้ ยกเว้นในกรณีต่อไปนี้

คอลัมน์จากการคำนวณไม่สามารถใช้เป็นข้อกำหนดข้อจำกัด DEFAULT หรือ FOREIGN KEY หรือใช้ร่วมกับข้อกำหนดข้อจำกัด NOT NULL อย่างไรก็ตาม คอลัมน์จากการคำนวณสามารถใช้เป็นคอลัมน์คีย์ดัชนีหรือเป็นส่วนหนึ่งของข้อจำกัดของคีย์หลักหรือ UNIQUE ได้ ถ้าค่าของคอลัมน์จากการคำนวณนั้นถูกกำหนดโดยนิพจน์ที่กำหนดขึ้น (คาดเดาได้) และอนุญาตให้ใช้ชนิดข้อมูลผลลัพธ์ในคอลัมน์ดัชนี .

เช่น ถ้าตารางมีคอลัมน์จำนวนเต็ม และ , คอลัมน์จากการคำนวณ ก+ขสามารถรวมไว้ในดัชนีและคอลัมน์จากการคำนวณได้ a+DATEPART(วว, GETDATE())- ไม่สามารถ เนื่องจากค่าอาจมีการเปลี่ยนแปลงในการโทรครั้งต่อๆ ไป

คอลัมน์ที่คำนวณแล้วไม่สามารถเป็นคอลัมน์เป้าหมายของคำสั่ง INSERT หรือ UPDATE

DBMS จะกำหนดโดยอัตโนมัติว่าคอลัมน์ที่คำนวณเป็น NULLable ตามนิพจน์ที่ใช้หรือไม่ ผลลัพธ์ของนิพจน์ส่วนใหญ่ถือเป็นโมฆะ แม้ว่าจะใช้เฉพาะคอลัมน์ที่ไม่สามารถเป็นโมฆะได้ เนื่องจากความเป็นไปได้ที่ล้นหรือความแม่นยำต่ำเกินไปอาจทำให้เกิดค่าว่างได้ เมื่อต้องการตรวจสอบว่าคอลัมน์ในตารางจากการคำนวณสามารถเป็น NULL ได้หรือไม่ ให้ใช้ฟังก์ชัน COLUMNPROPERTY กับคุณสมบัติ อนุญาตNull- คุณสามารถสร้างนิพจน์ที่ไม่เป็นโมฆะได้โดยระบุ ISNULL ด้วยค่าคงที่ ตรวจสอบ_การแสดงออกโดยที่ค่าคงที่คือค่าที่ไม่ใช่ค่าว่างซึ่งแทนที่ค่า NULL ใดๆ คอลัมน์ที่คำนวณตามนิพจน์ที่มีประเภท CLR ที่ผู้ใช้กำหนดจำเป็นต้องได้รับการอนุญาตประเภท REFERENCES

ระบุว่า SQL Server จะจัดเก็บค่าที่คำนวณได้ในตารางและอัปเดตเมื่อคอลัมน์ใดๆ ที่คอลัมน์จากการคำนวณขึ้นอยู่กับการเปลี่ยนแปลง การระบุ PERSISTED ในคอลัมน์จากการคำนวณทำให้คุณสามารถสร้างดัชนีในคอลัมน์จากการคำนวณที่กำหนดได้แต่ไม่ชัดเจน

ที่ การสร้างตารางนอกเหนือจากเทคนิคที่กล่าวถึงข้างต้น คุณสามารถระบุคีย์เวิร์ด CONSTRAINT ที่เป็นทางเลือก เพื่อให้ข้อจำกัดมีชื่อเฉพาะภายในฐานข้อมูลได้

สร้าง สามารถ คำสั่งซื้อ(

ID_คำสั่งซื้อINT ไม่เป็นโมฆะ ,

จาน INT ไม่เป็นโมฆะ

ปริมาณ_ เสิร์ฟ INT ไม่ใช่การตรวจสอบ NULL ( ปริมาณ_ เสิร์ฟ >0 ) ,

วันที่ วันที่ไม่เป็นโมฆะ ,

คีย์หลัก( ID_คำสั่งซื้อ, จาน, วันที่),

ข้อจำกัด ต่างประเทศ_KEY

กุญแจต่างประเทศ ( จาน, วันที่) ข้อมูลอ้างอิง เมนู( จาน, วันที่)) ;

จากนั้น คุณสามารถทำงานกับข้อจำกัดที่มีชื่อได้เช่นเดียวกับวัตถุฐานข้อมูล หากไม่ได้ระบุชื่อของข้อจำกัด เคอร์เนล SUDB จะสร้างมันขึ้นมาโดยอัตโนมัติ โดยเลือกชื่อตามกฎที่กำหนดไว้ในระบบ

ตัวอย่างของสคริปต์ที่สร้างโดยระบบเพื่อสร้างตาราง Catalog_type_of_dish

สร้างตาราง .[Directory_type_of_dish](

ตัวตน (1,1) ไม่เป็นโมฆะ

[ดู] (20) ไม่เป็นโมฆะ

ข้อจำกัดคีย์หลักที่คลัสเตอร์

) ด้วย (PAD_INDEX = ปิด, STATISTICS_NORECOMPUTE = ปิด, IGNORE_DUP_KEY = ปิด, ALLOW_ROW_LOCKS = เปิด, ALLOW_PAGE_LOCKS = เปิด) เปิด

ตัวตนคืออะไร

IDENTITY ไม่ใช่ประเภทข้อมูล นี่คือคุณสมบัติเพิ่มเติมบางประการ ซึ่งเป็นข้อจำกัดที่กำหนดกับประเภทข้อมูลจำนวนเต็มในเซิร์ฟเวอร์ MS SQL เหล่านั้น. คุณสมบัตินี้สามารถนำไปใช้กับฟิลด์ประเภทต่อไปนี้: จิ๋ว, ตัวเล็ก, ภายใน, ใหญ่, ทศนิยม(น,0), หรือ ตัวเลข(p,0)

คุณสมบัติที่เทียบเท่ากันใน FoxPro คือชนิดข้อมูล Integer-AutoIncrease อย่าคิดว่า Integer-AutoIncrease เป็นฟิลด์ที่มีคุณสมบัติ Identity ไม่เลย. นี่มันอนาล็อกชัดๆ โดยพื้นฐานแล้วจะคล้ายกัน แต่มีความแตกต่างหลายประการ ในบทความนี้เราจะพูดถึงคุณสมบัติ IDENTITY ในเซิร์ฟเวอร์ MS SQL

ฟิลด์ที่มีคุณสมบัติ IDENTITY มีคุณลักษณะดังต่อไปนี้:

  • ในตารางเดียว อนุญาตให้มีเพียงช่องเดียวที่มีคุณสมบัติ IDENTITY
  • ฟิลด์ที่มีคุณสมบัติ IDENTITY ไม่สามารถแก้ไขได้ พวกเขามีคุณสมบัติแบบอ่านอย่างเดียว
  • ฟิลด์ที่มีคุณสมบัติ IDENTITY จะถูกกำหนดค่าโดยอัตโนมัติเมื่อมีการสร้างเรกคอร์ดใหม่

มีคุณสมบัติอื่นๆ บางอย่าง แต่เป็นผลสืบเนื่องมาจากคุณสมบัติที่ระบุไว้อยู่แล้ว

ค่าใหม่คือค่าสุดท้ายที่ใช้บวกค่าคงที่บางส่วน โปรดทราบว่าค่าใหม่ไม่ได้ขึ้นอยู่กับค่าสูงสุดในรายการที่มีอยู่ แต่ขึ้นอยู่กับค่าสุดท้ายที่ใช้ ซึ่งหมายความว่าบันทึกที่มีค่าที่ใช้ล่าสุดอาจไม่มีอยู่จริง แต่ค่าจะยังคงถูกใช้อยู่

กล่าวอีกนัยหนึ่ง "หลุม" ค่อนข้างยอมรับได้ในลำดับค่าของฟิลด์ที่มีคุณสมบัติ IDENTITY รายการค่าไม่ต่อเนื่องกันเลย

โดยทั่วไป ขั้นตอนการเพิ่มขึ้นคือ 1 แต่อาจเป็นจำนวนเต็มใดๆ ก็ได้ รวมถึงสิ่งที่เป็นลบด้วย

เนื่องจากคุณลักษณะเหล่านี้ของฟิลด์ที่มีคุณสมบัติ IDENTITY ฟิลด์ดังกล่าวจึงมักถูกใช้เป็นคีย์หลัก กล่าวอีกนัยหนึ่ง เนื่องจากเขตข้อมูลที่มีค่าสามารถระบุบันทึกตารางได้โดยไม่ซ้ำกันเสมอ

โปรดทราบว่าคุณสมบัติ IDENTITY ไม่ได้ควบคุมความเป็นเอกลักษณ์ของข้อมูล แต่อย่างใด ตัวอย่างเช่นหากเดิมฟิลด์เป็นประเภท INTEGER และมีการป้อนค่าจำนวนหนึ่งลงไป จากนั้นโครงสร้างของตารางก็เปลี่ยนไปและมีการใช้คุณสมบัติ IDENTITY กับฟิลด์นี้ จากนั้นระเบียนใหม่ก็อาจมีข้อมูลเดียวกันกับที่ป้อนในตารางนี้ก่อนหน้านี้ ดังนั้น หากใช้ฟิลด์ที่มีคุณสมบัติ IDENTITY เป็นคีย์หลัก ก็ควรวางข้อจำกัดเอกลักษณ์เพิ่มเติมในฟิลด์นี้

ข้อเสียของการใช้ฟิลด์ที่มีคุณสมบัติ IDENTITY เป็นคีย์หลัก

อย่างไรก็ตาม แม้ว่าการใช้ฟิลด์ที่มีคุณสมบัติ IDENTITY เป็นคีย์หลักจะมีข้อดีที่ชัดเจน แต่ก็มีข้อเสียเปรียบร้ายแรงเช่นกัน

ไม่สามารถทราบค่าของเขตข้อมูลที่มีคุณสมบัติ IDENTITY ได้จนกว่าจะสร้างบันทึกจริง

แล้วไงล่ะ? ปัญหาอะไร? มาสร้างบันทึกและค้นหามูลค่าใหม่กันดีกว่า

ปัญหาคือในการหาค่าของฟิลด์ในเรคคอร์ด จะต้องค้นหาเรคคอร์ดนี้ก่อน และการค้นหาบันทึกจะดำเนินการอย่างแม่นยำตามค่าของคีย์หลัก ผู้ที่ต้องกำหนดคุณค่า วงจรอุบาทว์: หากต้องการอ่านค่า คุณต้องรู้ค่านี้!

โครงสร้างการจัดเก็บข้อมูลในเซิร์ฟเวอร์ MS SQL นั้นแตกต่างโดยพื้นฐานจากโครงสร้างการจัดเก็บข้อมูลในไฟล์ DBF ไม่มีแนวคิดเช่น "หมายเลขบันทึกทางกายภาพ" "บันทึกถัดไป" "บันทึกล่าสุด" ฯลฯ เหล่านั้น. ไม่สามารถไปที่ "บันทึกสุดท้าย" เพื่ออ่านค่าคีย์หลักได้

ยิ่งไปกว่านั้น แม้ว่าค่าใหม่ของฟิลด์ที่มีคุณสมบัติ IDENTITY จะมากกว่าค่าใด ๆ ที่มีอยู่เสมอ (หากการเพิ่มขึ้นเป็นจำนวนบวก) ก็เป็นไปไม่ได้ที่จะกำหนดค่าใหม่นี้โดยเพียงแค่คำนวณค่าสูงสุดของ ค่าที่มีอยู่ ไม่ แน่นอนว่าจะได้ค่าสูงสุดมาเอง ไม่มีการรับประกันว่าค่าผลลัพธ์จะเป็นมูลค่าของบันทึกที่สร้างขึ้นอย่างแน่นอน

ประเด็นก็คือ ตามกฎแล้ว เซิร์ฟเวอร์ MS SQL ถูกใช้ในแอปพลิเคชันที่มีผู้ใช้หลายคน ซึ่งหมายความว่าผู้ใช้หลายคนสามารถสร้างเรกคอร์ดใหม่ได้ในเวลาเดียวกัน ปรากฎว่าผู้ใช้รายหนึ่งสร้างบันทึกใหม่ จากนั้นเริ่มคำนวณค่าสูงสุด และในขณะนั้นผู้ใช้อีกรายก็สร้างบันทึกใหม่ด้วย เป็นผลให้ผู้ใช้รายแรกจะได้รับค่าของเรกคอร์ดที่สร้างโดยผู้ใช้คนที่สองเป็นค่าสูงสุด

เราควรหยุดใช้ช่องที่มีคุณสมบัติ IDENTITY เป็นคีย์หลักหรือไม่ ไม่เลย. ยังมีวิธีกำหนดค่าของเขตข้อมูลด้วยคุณสมบัติ IDENTITY ของระเบียนใหม่

วิธีกำหนดค่าของเขตข้อมูลด้วยคุณสมบัติ IDENTITY ในระเบียนใหม่

จริงๆ แล้ว มีกลยุทธ์พื้นฐานสามประการในการกำหนดค่าของฟิลด์ที่มีคุณสมบัติ IDENTITY ในเรกคอร์ดใหม่ที่เพิ่งสร้างขึ้น

ตอนนี้เรามาดูข้อดีและข้อเสียของแต่ละกลยุทธ์กันดีกว่า

ค่าที่ส่งคืนโดยตัวแปรระบบ @@IDENTITY

MS SQL Server มีตัวแปรระบบจำนวนหนึ่ง ซึ่งค่าจะเปลี่ยนแปลงโดยอัตโนมัติเมื่อมีเหตุการณ์บางอย่างเกิดขึ้น โดยเฉพาะอย่างยิ่ง ค่าของตัวแปรระบบ @@IDENTITY จะถูกตั้งค่าเป็นค่าของฟิลด์โดยอัตโนมัติด้วยคุณสมบัติ IDENTITY ของบันทึกที่สร้างขึ้นล่าสุดในการเชื่อมต่อปัจจุบัน เหล่านั้น. การสร้างรายการใหม่ในการเชื่อมต่ออื่น (โดยผู้ใช้รายอื่น) จะไม่ส่งผลกระทบต่อมูลค่าของมันในทางใด ๆ ในการเชื่อมต่อนี้

เอาล่ะ นี่คือวิธีแก้ปัญหา หลังจากสร้างบันทึกใหม่แล้ว เราก็อ่านค่าของตัวแปรระบบ @@IDENTITY และได้ค่าที่ต้องการ

โดยทั่วไปแล้วจริง ปัญหาเดียวคือตัวแปรระบบ @@IDENTITY เปลี่ยนค่าเมื่อสร้างรายการ ใดๆโต๊ะ.

ในทางปฏิบัติ หมายความว่าหากมีการติดตั้งทริกเกอร์การแทรกบนตาราง เนื้อความประกอบด้วยคำสั่ง INSERT เพื่อสร้างบันทึกในตารางอื่น ซึ่งในทางกลับกันก็มีฟิลด์ที่มีคุณสมบัติ IDENTITY ด้วยเช่นกัน จากนั้น @@ ตัวแปรระบบ IDENTITY จะได้รับค่าของฟิลด์จากตารางที่สองนี้

กล่าวอีกนัยหนึ่ง คุณสามารถพึ่งพาค่าของตัวแปรระบบ @@IDENTITY ได้ แต่โปรดจำไว้ว่าตัวแปรนี้ไม่ได้เชื่อมโยงกับค่าของฟิลด์ในตารางเดียว

ส่งคืนค่าจาก SCOPE_IDENTITY()

ใน MS SQL 2000 มีการแนะนำฟังก์ชันระบบ SCOPE_IDENTITY() ฟังก์ชันนี้ยังส่งกลับค่าของฟิลด์ที่มีคุณสมบัติ IDENTITY ของบันทึกล่าสุดที่สร้างขึ้น แต่สร้างขึ้นภายใน SCOPE ปัจจุบัน

การแปลคำว่า SCOPE เป็นภาษารัสเซียค่อนข้างยาก แต่เราสามารถพูดได้โดยประมาณว่า SCOPE เป็นขั้นตอนหรือฟังก์ชันเดียว กล่าวอีกนัยหนึ่ง SCOPE_IDENTITY() จะส่งกลับค่าของฟิลด์ที่มีคุณสมบัติ IDENTITY ของบันทึกล่าสุดที่สร้างขึ้นภายในขั้นตอนที่เรียกใช้ฟังก์ชันนี้

ทริกเกอร์เป็น SCOPE อื่นอยู่แล้ว (ฟังก์ชันอื่น) ดังนั้นจึงไม่ส่งผลต่อค่าที่ส่งคืนโดย SCOPE_IDENTITY()

แม้ว่าผู้ใช้สองคนจะเรียกขั้นตอนเดียวกันในเวลาเดียวกัน แต่แต่ละคนก็เรียกขั้นตอนในขอบเขตของตนเอง เหล่านั้น. อีกครั้งไม่มีความขัดแย้ง

ข้อเสียของฟังก์ชันนี้คือควรถูกเรียกใช้ภายใน SCOPE ซึ่งเป็นที่ซึ่งมีการสร้างบันทึกใหม่ของตารางที่เราสนใจ และนี่ไม่สามารถทำได้เสมอไป

กล่าวอีกนัยหนึ่ง หากต้องการใช้ SCOPE_IDENTITY() อย่างถูกต้อง คุณจะต้องทราบขอบเขตของ SCOPE อยู่เสมอ บ่อยครั้งด้วยการสร้างขั้นตอนพิเศษ

ค้นหาเรกคอร์ดใหม่ตามค่าของฟิลด์อื่น

หากคุณจำได้ ปัญหาหลักในการกำหนดค่าของฟิลด์ที่มีคุณสมบัติ IDENTITY คือฟิลด์นี้จะถูกใช้เป็นคีย์หลัก เหล่านั้น. พบรายการที่จำเป็นตามค่าของมัน

อย่างไรก็ตาม ตารางมักจะมีเขตข้อมูลหรือชุดเขตข้อมูลที่สามารถใช้เพื่อระบุระเบียนโดยไม่ซ้ำกันได้เช่นกัน ตัวอย่างเช่น หากเรากำลังพูดถึงไดเร็กทอรี แน่นอนว่าไดเร็กทอรีนั้นจะต้องมีช่อง "ชื่อ" เห็นได้ชัดว่าฟิลด์นี้จะต้องไม่ซ้ำกันภายในไดเร็กทอรี ไม่เช่นนั้นจุดสำคัญของการใช้หนังสืออ้างอิงก็จะหายไป เหตุใดจึงต้องป้อนรายการที่มีความหมายเหมือนกันลงในไดเร็กทอรี?

ทำไมไม่ใช้ "ชื่อ" นี้เป็นคีย์หลักล่ะ เหตุใดคุณจึงต้องมีฟิลด์ที่มีคุณสมบัติ IDENTITY นี่เป็นหัวข้อสำหรับการสนทนาอื่น กล่าวโดยย่อ "ชื่อ" มีไว้สำหรับผู้ใช้ (ข้อมูลภายนอก) และ IDENTITY มีไว้สำหรับรับรองความสมบูรณ์ในการอ้างอิงของฐานข้อมูล (ข้อมูลภายใน)

ไม่ทราบค่าของฟิลด์ที่มีคุณสมบัติ IDENTITY ในระเบียนใหม่ แต่ความหมายของฟิลด์ Title ในบันทึกใหม่นี้ค่อนข้างเป็นที่รู้จัก ผู้ใช้เข้ามาเอง! ซึ่งหมายความว่าหลังจากสร้างบันทึกใหม่ คุณสามารถค้นหาบันทึกใหม่นี้ตามค่าของฟิลด์ "ชื่อ" และอ่านค่าของฟิลด์ด้วยคุณสมบัติ IDENTITY

ปัญหาเดียวคือไม่มีฟิลด์หรือชุดฟิลด์ดังกล่าวเพื่อระบุเรกคอร์ดโดยไม่ซ้ำกันเสมอไป นี่เป็นเหตุผลหนึ่งในการป้อนสิ่งที่เรียกว่าคีย์ตัวแทน ช่องเดียวกันเหล่านั้นที่มีคุณสมบัติ IDENTITY

อย่างไรก็ตาม หากคุณตัดสินใจที่จะใช้กลยุทธ์นี้เพื่อค้นหาบันทึกใหม่ ตรวจสอบให้แน่ใจว่าได้กำหนดข้อจำกัดที่ไม่ซ้ำกันในช่อง "ชื่อเรื่อง" (หรือชุดของช่องที่คุณเลือก) นั่นคือเพื่อไม่ให้มีบันทึกสองรายการที่มีค่าเท่ากันในฟิลด์นี้โดยบังเอิญ

วิธีทำงานกับฟิลด์ที่มีคุณสมบัติ IDENTITY ใน FoxPro

เราจบส่วนทางทฤษฎีแล้ว ตอนนี้ "เรามาลองเอาเรื่องพวกนี้ออกไปกันดีกว่า" เหล่านั้น. เรามาตัดสินใจว่าจะใช้ความรู้ทั้งหมดนี้ใน FoxPro อย่างไร ให้เราชี้แจงปัญหาที่ต้องแก้ไขอีกครั้ง

บันทึกจะถูกเพิ่มลงในตารางเซิร์ฟเวอร์ MS SQL ที่มีเขตข้อมูลที่มีคุณสมบัติ IDENTITY ทันทีหลังจากสร้างเรกคอร์ดใหม่ คุณจะต้องรับค่าของฟิลด์ด้วยคุณสมบัติ IDENTITY บนฝั่ง FoxPro

FoxPro มีสามตัวเลือกพื้นฐานสำหรับการจัดระเบียบงานกับเซิร์ฟเวอร์ MS SQL

  • การใช้มุมมองระยะไกล
  • การใช้อะแดปเตอร์เคอร์เซอร์

ที่นี่เราควรพิจารณาว่าเหตุการณ์ใดที่สร้างบันทึกบนเซิร์ฟเวอร์ MS SQL ทุกอย่างชัดเจนด้วย Pass-Trough นี่เป็นคำสั่งโดยตรงไปยังเซิร์ฟเวอร์เพื่อสร้างบันทึกใหม่ แต่ด้วย Remote View และ Cursor Adapter จะแตกต่างออกไปเล็กน้อย

ผลลัพธ์ของทั้ง Remote View และ Cursor Adapter คือเคอร์เซอร์ เหล่านั้น. ตารางชั่วคราวที่อยู่บนเครื่องของลูกค้า ตามค่าเริ่มต้น เคอร์เซอร์นี้จะเปิดโดยอัตโนมัติในโหมดบัฟเฟอร์แถวในแง่ดี (3) และสามารถเปลี่ยนเป็นโหมดบัฟเฟอร์ตารางในแง่ดีเท่านั้น (5) เป็นไปไม่ได้ที่จะเปลี่ยนไปใช้โหมดการบัฟเฟอร์ในแง่ร้ายหรือปิดใช้งานการบัฟเฟอร์โดยสิ้นเชิงสำหรับเคอร์เซอร์นี้

ด้วยเหตุนี้ เรคคอร์ดใหม่จะถูกสร้างขึ้นทางกายภาพบนเครื่องไคลเอนต์ในเคอร์เซอร์นี้ แม่นยำยิ่งขึ้นในบัฟเฟอร์ของเคอร์เซอร์นี้ การสร้างบันทึกทางกายภาพบนเซิร์ฟเวอร์ MS SQL จะเกิดขึ้นหลังจากรีเซ็ตบัฟเฟอร์แล้วเท่านั้น

สำหรับการบัฟเฟอร์บรรทัด การล้างบัฟเฟอร์สามารถเกิดขึ้นได้โดยอัตโนมัติเมื่อคุณทำอย่างใดอย่างหนึ่งต่อไปนี้:

  • กระโดด (หรือพยายามกระโดด) ไปยังรายการอื่น
  • การปิดเคอร์เซอร์
  • เปลี่ยนเป็นโหมดบัฟเฟอร์ตาราง
  • โดยคำสั่ง TableUpdate()

สำหรับการบัฟเฟอร์ตาราง บัฟเฟอร์สามารถล้างได้โดยใช้คำสั่ง TableUpdate() เท่านั้น และไม่ต้องทำอะไรอย่างอื่นอีก

ไม่มีการดำเนินการหรือการดำเนินการอื่นๆ ด้วย Remote View หรือ Cursor Adapter ที่จะนำไปสู่การสร้างบันทึกใหม่บนเซิร์ฟเวอร์ MS SQL เมื่อดำเนินการใด ๆ หากปรากฎว่ามีการสร้างบันทึกใหม่บนเซิร์ฟเวอร์ MS SQL ซึ่งหมายความว่าเคอร์เซอร์อยู่ในโหมดบัฟเฟอร์บรรทัดและเกิดเหตุการณ์หนึ่งที่ทำให้เกิดการรีเซ็ตบัฟเฟอร์อัตโนมัติ

ตัวอย่างเช่น สิ่งนี้สามารถเกิดขึ้นได้กับคำสั่ง Requery() สำหรับมุมมองระยะไกล แต่นี่ไม่ได้หมายความว่าคำสั่ง Requery() จะล้างบัฟเฟอร์ ไม่เลย. ง่ายๆ ก็คือ เงื่อนไขประการหนึ่งในการรันคำสั่ง Requery() ก็คือการปิดเคอร์เซอร์ที่มีอยู่ก่อนหน้านี้ แต่เหตุการณ์นี้จะทำให้เกิดการรีเซ็ตบัฟเฟอร์อัตโนมัติหากเคอร์เซอร์อยู่ในโหมดบัฟเฟอร์บรรทัด

เพื่อหลีกเลี่ยงความเข้าใจผิดดังกล่าว ให้เปลี่ยนเคอร์เซอร์ไปที่โหมดบัฟเฟอร์ตาราง (5) ในกรณีนี้ คุณจะสามารถควบคุมกระบวนการรีเซ็ตบัฟเฟอร์ได้ตลอดเวลา

อย่างไรก็ตาม คุณควรเข้าใจว่าแม้ว่าคุณจะตั้งค่าโหมดการบัฟเฟอร์ตาราง เปลี่ยนเรคคอร์ดหลายรายการในมุมมองระยะไกลหรืออะแดปเตอร์เคอร์เซอร์ จากนั้นออกคำสั่ง TableUpdate() บัฟเฟอร์จะยังคงถูกล้างทีละเรกคอร์ด เหล่านั้น. ไม่ใช่คำสั่งเดียวที่จะถูกส่งไปยังเซิร์ฟเวอร์ เช่น เพื่อการแก้ไข แต่เป็นชุดคำสั่งสำหรับแก้ไขแต่ละบันทึกแยกกัน

ในส่วนที่เกี่ยวข้องกับการดำเนินการสร้างเรกคอร์ดใหม่ จะเป็นไปตามนั้นในเหตุการณ์ทั้งหมดของอ็อบเจ็กต์ Cursor Adapter เสมอแทรกเพียงระเบียนเดียวในแต่ละครั้ง

การใช้เทคโนโลยี Pass-Through โดยตรงผ่านฟังก์ชัน SQLEXEC()

ด้วยวิธีการทำงานนี้ โปรแกรมเมอร์จะทำงานโดยตรงกับเซิร์ฟเวอร์ MS SQL โดยจะสร้างคำสั่งทั้งหมดที่ส่งไปยังเซิร์ฟเวอร์ รับผลลัพธ์ และประมวลผลด้วยตัวมันเอง ในกรณีนี้ การส่งคำขอเพิ่มเติมไปยังเซิร์ฟเวอร์สำหรับค่าของฟังก์ชัน SCOPE_IDENTITY ไม่ใช่เรื่องยาก

ท้องถิ่น lcNewValue, lnResut lcNewValue = "ค่าใหม่" lnResut = SQLExec(m.lnConnectHandle,"INSERT INTO MyTab (Field1) VALUES (?m.lcNewValue)") IF m.lnResut>0 SQLExec(m.lnConnectHandle,"SELECT NewIdent=SCOPE_IDENTITY()","NewIdent") ?NewIdent.NewIdent ELSE LOCAL laError(1) =AERROR(laError) * Анализ массива laError для уточнения причины ошибки ENDIF !}

ในตัวอย่างนี้ m.lnConnectHandle คือตัวเลข ซึ่งเป็นจำนวนการเชื่อมต่อกับเซิร์ฟเวอร์ MS SQL ที่ได้รับการกำหนดค่าก่อนหน้านี้ MyTab เป็นตารางที่มีเขตข้อมูลที่มีคุณสมบัติ IDENTITY

หลังจากดำเนินการแบบสอบถามที่สองในเคอร์เซอร์ผลลัพธ์ NewIdent ในฟิลด์ NewIdent ของบันทึกแรก เราได้รับค่าที่ต้องการ ในรูปแบบนี้ ทั้งคำสั่ง insert และการเรียกฟังก์ชัน SCOPE_IDENTITY() จะเกิดขึ้นใน SCOPE เดียวกัน ดังนั้นเราจึงได้ค่าที่ต้องการ

การใช้มุมมองระยะไกล

มุมมองระยะไกลเป็น "ส่วนเสริม" ชนิดหนึ่งสำหรับเทคโนโลยีส่งผ่าน โดยพื้นฐานแล้ว เมื่อสร้างบันทึกใหม่ คำสั่ง INSERT INTO เดียวกันจะถูกดำเนินการ อย่างไรก็ตาม ปัญหาคือแม้ว่าเราจะอ่านหมายเลขการเชื่อมต่อที่ Remote View กำลังทำงานอยู่ แล้วเรียกใช้แบบสอบถามเพื่อกำหนดค่าที่ส่งคืนโดย SCOPE_IDENTITY() เราก็จะได้รับ NULL เพราะในกรณีนี้ คำสั่ง insert และ SCOPE_IDENTITY() ดำเนินการในขอบเขตที่แตกต่างกัน ดังนั้นจึงมีเพียงสองวิธีเท่านั้นในการกำหนดค่าของเขตข้อมูลที่มีคุณสมบัติ IDENTITY

* การกำหนดค่าของตัวแปรระบบ @@IDENTITY LOCAL lnConnectHandle lnConnectHandle = CursorGetProp("ConnectHandle","MyRemoteView") SQLExec(m.lnConnectHandle,"SELECT NewIdent=@@IDENTITY","NewIdent") ?NewIdent.NewIdent * การกำหนด ตามค่าของฟิลด์อื่น LOCAL lnConnectHandle, lcNickName lnConnectHandle = CursorGetProp("ConnectHandle","MyRemoteView") lcNickName = MyRemoteView.NickName SQLExec(m.lnConnectHandle,"SELECT TabId FROM MyTab WHERE NickName=?lcNickName","NewIdent") ? NewIdent.TabId

ในทั้งสองตัวอย่าง MyRemoteView คือชื่อของมุมมองระยะไกลของคุณ NickName คือชื่อของฟิลด์ในมุมมองระยะไกลตามค่าที่ใช้ค้นหาระเบียนใหม่ และ TabID เป็นฟิลด์เดียวกันกับคุณสมบัติ IDENTITY ที่ต้องกำหนดค่า

ถือว่ามีการสร้างบันทึกใหม่เกิดขึ้นแล้ว มีการออกคำสั่ง TableUpdate() อย่างชัดเจน หรือมุมมองระยะไกลอยู่ในโหมดบัฟเฟอร์บรรทัด และมีความพยายามที่จะย้ายไปยังระเบียนอื่น

และเหตุใดในกรณีที่สองจึงไม่ใช่โซลูชันการค้นหาที่ชัดเจนกว่าที่ใช้หลังจาก Requery() สิ่งที่ต้องการ

คำขอ ("MyRemoteView") เลือก MyRemoteView ค้นหาตำแหน่งสำหรับชื่อเล่น = "ค่าใหม่" ?MyRemoteView.TabID

มีหลายสาเหตุนี้.

ขั้นแรก Requery() เกี่ยวข้องกับการดำเนินการค้นหาอีกครั้ง เหล่านั้น. ถือว่าบัฟเฟอร์ของรายการ Remote View ทั้งหมดถูกล้างไปแล้ว แต่นี่อาจไม่เป็นเช่นนั้น ตัวอย่างเช่น พวกเขาจะล้างบัฟเฟอร์ทีละระเบียนในลูป

ประการที่สอง มุมมองระยะไกลมักจะมีเงื่อนไขเพิ่มเติมสำหรับการเลือกบันทึก และบันทึกที่สร้างขึ้นใหม่อาจไม่รวมอยู่ในเงื่อนไขการเลือกเลย เหล่านั้น. หลังจาก Requery() เรกคอร์ดแม้ว่าจะถูกสร้างขึ้นจริงบนเซิร์ฟเวอร์ แต่ก็จะไม่แสดงใน Remote View เอง

การใช้อะแดปเตอร์เคอร์เซอร์

วัตถุอะแดปเตอร์เคอร์เซอร์ถูกนำมาใช้ใน FoxPro โดยเริ่มจาก Visual FoxPro 8 ซึ่งได้รับการออกแบบมาเพื่อให้ทำงานกับข้อมูลระยะไกลได้ง่ายขึ้นเมื่อดำเนินการบางอย่างทั่วไป โดยเฉพาะการดำเนินการสร้างสถิติใหม่ คุณสามารถใช้ทั้งสามตัวเลือกเพื่อรับค่าของฟิลด์ที่มีคุณสมบัติ IDENTITY ในเรกคอร์ดใหม่ผ่าน Cursor Adapter

ค่าที่ส่งคืนโดยตัวแปรระบบ @@IDENTITY

หลังจากที่บัฟเฟอร์ถูกรีเซ็ตและมีการสร้างเรกคอร์ดใหม่บนเซิร์ฟเวอร์ MS SQL เหตุการณ์ AfterInsert จะเริ่มทำงานในวัตถุ Cursor Adapter นี่คือที่ที่คุณต้องส่งคำขอเพิ่มเติมไปยังเซิร์ฟเวอร์และอ่านค่าของตัวแปรระบบ @@IDENTITY

ขั้นตอนหลังจากใส่ LPARAMETERS cFldState, lForce, cInsertCmd, lResult IF lResult=.T. การแทรก && สำเร็จ เราจำเป็นต้องได้รับค่า ID LOCAL currentArea currentArea=SELECT() TRY IF 1=SQLEXEC(this.DataSource,"SELECT NewIdent=@@IDENTITY", "NewIdent") REPLACE TabId WITH NewIdent.NewIdent IN (This.Alias) USE IN IDRes ELSE LOCAL laError(1) =AERROR(laError) * วิเคราะห์อาร์เรย์ laError เพื่อระบุสาเหตุของข้อผิดพลาด ENDIF FINALLY SELECT (พื้นที่ปัจจุบัน) ENDTRY ENDIF ENDPROC

ฉันคิดว่ารหัสค่อนข้างชัดเจนและไม่ต้องการคำอธิบาย หลังจากสร้างบันทึกใหม่บนเซิร์ฟเวอร์สำเร็จโดยใช้ SQLExec() สำหรับการเชื่อมต่อเดียวกัน เราได้กำหนดค่า @@IDENTITY และเขียนค่านี้ลงในบันทึกเคอร์เซอร์ปัจจุบันในฟิลด์คีย์

ใน Visual FoxPro 9 คุณสมบัติถูกเพิ่มลงในวัตถุอะแดปเตอร์เคอร์เซอร์ แทรกCmdRefreshCmd- นี่คือคำสั่งที่ส่งไปยังเซิร์ฟเวอร์หลังจากแทรกบันทึกใหม่สำเร็จแล้วเท่านั้น เหล่านั้น. ไม่จำเป็นต้องตรวจสอบว่ามีการสร้างบันทึกใหม่แล้ว

จับคู่กับคุณสมบัติใหม่อื่น แทรก CmdRefreshFieldListคุณสามารถดำเนินการอัปเดตตามค่าของตัวแปรระบบ @@IDENTITY ได้ง่ายขึ้น เมื่อต้องการทำเช่นนี้ คุณเพียงแค่ต้องทำการตั้งค่าต่อไปนี้สำหรับวัตถุ CursorAdapter

CursorAdapter.InsertCmdRefreshCmd = "SELECT @@IDENTITY" CursorAdapter.InsertCmdRefreshFieldList = "TabId"

ในที่นี้ TabId ไม่ใช่ชื่อของฟิลด์ที่มีคุณสมบัติ IDENTITY ในตารางบนเซิร์ฟเวอร์ MS SQL แต่เป็นชื่อของฟิลด์เคอร์เซอร์ที่ได้รับบนฝั่งไคลเอ็นต์ซึ่งเนื้อหาของฟิลด์ที่มีคุณสมบัติ IDENTITY จะปรากฏขึ้น ตามกฎแล้วชื่อเหล่านี้จะเหมือนกัน แต่โดยทั่วไปอาจแตกต่างกัน รายละเอียดเพิ่มเติมเกี่ยวกับที่พัก แทรก CmdRefreshFieldListอ่านด้านล่าง. ในส่วนการค้นหาบันทึกใหม่ตามค่าของฟิลด์อื่น

ส่งคืนค่าจาก SCOPE_IDENTITY()

ที่นี่ทุกอย่างซับซ้อนขึ้นเล็กน้อย ประเด็นก็คือคุณไม่สามารถดำเนินการในลักษณะเดียวกับในกรณีของการกำหนดตัวแปรระบบ @@IDENTITY SQLExec() และ Cursor Adapter ทำงานใน SCOPE ที่แตกต่างกัน ดังนั้น ด้วยแนวทางนี้ SCOPE_IDENTITY() จะส่งคืนค่า NULL เสมอ เพื่อเอาชนะความขัดแย้งนี้ จึงมีการใช้กระบวนการพิเศษ

ขั้นแรก คุณต้องสร้างขั้นตอนการจัดเก็บต่อไปนี้บนเซิร์ฟเวอร์ MS SQL เอง

สร้างขั้นตอน Get_ValueInt @ValueIn Int, @ValueOut Int OUTPUT ตามชุด NOCOUNT บน SELECT @ValueOut=@ValueIn

ดังที่คุณเห็น วัตถุประสงค์ของขั้นตอนนี้คือเพียงเพื่อกำหนดค่าของพารามิเตอร์อินพุตให้กับพารามิเตอร์เอาต์พุต ตอนนี้คุณสามารถใช้ขั้นตอนนี้เพื่อกำหนดค่าของ SCOPE_IDENTITY() ในอะแดปเตอร์เคอร์เซอร์ ที่จะทำเช่นนี้ภายในงาน ก่อนใส่จะมีการทดแทนดังกล่าว

ขั้นตอนก่อนใส่ LPARAMETERS cFldState, lForce, cInsertCmd cInsertCmd = cInsertCmd + ; "; ประกาศ @id int" + ; "; SELECT @id=SCOPE_IDENTITY()" + ; "; EXEC Get_ValueInt @id, [ป้องกันอีเมล]“เอ็นด์โปรค

เพียงจำไว้ว่าในการทดแทนนี้ MyTab จะไม่ใช่ชื่อของตารางบนเซิร์ฟเวอร์ MS SQL อีกต่อไป แต่เป็นชื่อของเคอร์เซอร์ที่สร้างขึ้นบนไคลเอนต์ ชื่อที่เขียนอย่างแม่นยำยิ่งขึ้นในคุณสมบัตินามแฝงของ Cursor Adapter นั้นเอง ดังนั้น "TabId" คือชื่อของฟิลด์เคอร์เซอร์ที่สร้างขึ้นบนไคลเอนต์และมีค่าของฟิลด์ที่มีคุณสมบัติ IDENTITY

ในกรณีนี้ โดยพื้นฐานแล้ว กระบวนการจัดเก็บแบบไดนามิกจะถูกสร้างขึ้น เหล่านั้น. ไม่ใช่แค่คำสั่ง INSERT เพียงคำสั่งเดียว แต่เป็นลำดับของคำสั่ง คำสั่งต่างๆ จะแยกจากกันด้วยเครื่องหมายอัฒภาค แม้ว่าจะไม่จำเป็นก็ตาม ก็เพียงพอแล้วที่จะแยกคำสั่งออกจากกันด้วยช่องว่างที่เรียบง่าย

ค้นหาเรกคอร์ดใหม่ตามค่าของฟิลด์อื่น

หากคุณมี Visual FoxPro 8 คุณสามารถใช้วิธีการที่คล้ายกับวิธีการที่ใช้ในการค้นหาค่าของตัวแปรระบบ @@IDENTITY เหล่านั้น. ในเมธอด AfterInsert ของออบเจ็กต์ Cursor Adapter ให้ทำการสืบค้นเพิ่มเติมผ่าน SQLExec()

เริ่มต้นด้วย Visual FoxPro 9 วัตถุอะแดปเตอร์เคอร์เซอร์มีคุณสมบัติเพิ่มเติมที่ช่วยให้คุณสามารถดำเนินการขั้นตอนนี้โดยอัตโนมัติโดยไม่ต้องเขียนโค้ดเพิ่มเติม

แทรก CmdRefreshFieldList- รายการฟิลด์ที่จะอัพเดตค่าหลังการแทรก
แทรกCmdRefreshKeyFieldList- ฟิลด์หรือชุดของฟิลด์ที่ไม่ใช่คีย์ซึ่งระบุเรคคอร์ดโดยไม่ซ้ำกันซึ่งจะถูกใช้เพื่อค้นหาเรคคอร์ดใหม่

ควรสังเกตว่าคุณสมบัติเหล่านี้ไม่ได้ระบุชื่อของเขตข้อมูลตารางของเซิร์ฟเวอร์ MS SQL เอง แต่เป็นชื่อเขตข้อมูลที่สอดคล้องกันของเคอร์เซอร์ที่ได้รับทางฝั่งไคลเอ็นต์ สำหรับตัวอย่างเดียวกันก็จะมีลักษณะดังนี้:

InsertCmdRefreshFieldList = "TabID" InsertCmdRefreshKeyFieldList = "NickName"

กล่าวอีกนัยหนึ่ง ขึ้นอยู่กับค่าของฟิลด์ NickName ระเบียนจะพบได้ในตารางบนเซิร์ฟเวอร์ MS SQL และค่าของฟิลด์ TabID จะถูกอ่าน หลังจากนั้นค่านี้จะถูกเขียนไปยังระเบียนใหม่ใน ด้านลูกค้า.


คุณสามารถดูการสนทนาโดยละเอียดเพิ่มเติมเกี่ยวกับหัวข้อนี้และตัวอย่างการใช้งานได้ที่นี่



วลาดิมีร์ มักซิมอฟ
อัปเดตครั้งล่าสุด: 05/01/06



มีคำถามหรือไม่?

แจ้งการพิมพ์ผิด

ข้อความที่จะส่งถึงบรรณาธิการของเรา: