อัปเดตเรกคอร์ดโดยไม่ต้องสอบถามก่อน?


107

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

มีวิธีใดบ้างที่ฉันสามารถอัปเดตบันทึกฐานข้อมูลโดยไม่ต้องดึงข้อมูลบันทึกของแต่ละรายการ

นี่คือตัวอย่างวิธีที่ฉันกำลังทำอยู่:

dataItem itemToUpdate = (from t in dataEntity.items
                                 where t.id == id
                                 select t).FirstOrDefault();

จากนั้นหลังจากดึงบันทึกฉันจะอัปเดตค่าบางอย่างในรายการและผลักดันบันทึกกลับ:

itemToUpdate.itemstatus = newStatus;
dataEntity.SaveChanges();

ฉันคิดว่าจะมีวิธีที่ดีกว่านี้มีความคิดใดบ้าง


2
มันไม่ใช่วิธีที่แย่มากในการทำสิ่งต่างๆ คุณมีสิทธิ์เข้าถึงตารางนั้นพร้อมกันหรือไม่?
Henk Holterman

ฉันคิดว่านี่คือการใช้งานที่ ORM อย่าง EF มีให้บริการ เพื่อให้การดำเนินการภายในบริบทของแอ็พพลิเคชันสามารถดำเนินการกับอ็อบเจ็กต์ที่คุณต้องการสร้าง / แก้ไข / ลบโดยไม่ต้องกังวลกับการนำฐานข้อมูลไปใช้งาน?
Pero P.

43
ฉันคิดว่าสำหรับนักพัฒนาที่มีภูมิหลังใน TSQL พยายามยอมรับและยอมรับ ORM มันไม่มีประสิทธิภาพเล็กน้อยในการค้นหาบันทึกเพื่ออัปเดตเท่านั้นและไม่เคยใช้ข้อมูลที่ดึงมา แนวคิดที่ว่านักพัฒนาไม่จำเป็นต้องเกี่ยวข้องกับการนำฐานข้อมูลไปใช้งานนี้ถือเป็นเรื่องน่าเบื่อ ยิ่งนักพัฒนารู้เกี่ยวกับระบบทั้งหมดมากเท่าไหร่ก็จะสามารถแก้ปัญหาได้ดีขึ้น ตัวเลือกไม่เคยเป็นสิ่งเลวร้าย
barrypicker

1
วิธี ORM นั้นใช้ได้ดีสำหรับวัตถุจริง แต่ถ้าคุณเก็บสิ่งอื่น ๆ ไว้ในฐานข้อมูลของคุณด้วย (เช่น binary blobs ขนาดใหญ่) จะมีประโยชน์อย่างยิ่งที่จะสามารถอัปเดตได้โดยไม่ต้องโหลดเนื้อหาต้นฉบับก่อน
BrainSlugs83

คำตอบ:


69

คุณควรใช้เมธอดAttach ()

การติดและถอดวัตถุ


17
คุณช่วยยกตัวอย่างได้ไหม
Bart Calixto

17
context.Products.Attach (ผลิตภัณฑ์); context.Entry (ผลิตภัณฑ์) .State = EntityState.Modified;
Gabriel

7
@ Gabriel สิ่งนี้จะไม่อัปเดตคุณสมบัติทั้งหมดหรือไม่? จะเกิดอะไรขึ้นถ้าฉันต้องการแก้ไขเพียงรายการเดียว?
David Pfeffer

23
ใช่สิ่งนี้จะอัปเดตคุณสมบัติทั้งหมด หากคุณต้องการอัพเดตคุณสมบัติเดียวคุณสามารถทำได้: context.Entry (ผู้ใช้) .Property (x => x.Property) .IsModified = true; (ดูที่นี่stackoverflow.com/a/5567616/57369 )
Gabriel

6
ฉันแค่ต้องการเพิ่มบริบทนั้นรายการ () มีให้ใน. net 4.1 เท่านั้นหากคุณยังคงใช้ 4.0 (เช่นฉัน) ให้ตรวจสอบสิ่งนี้สำหรับทางเลือก: stackoverflow.com/questions/7113434/where-is- รายการบริบท ซึ่งโดยพื้นฐานแล้ว: context.ObjectStateManager.ChangeObjectState (yourObject, EntityState.Modified);
dyslexicanaboko

40

คุณยังสามารถใช้ Direct SQL กับฐานข้อมูลโดยใช้บริบทของพื้นที่เก็บข้อมูล ตัวอย่าง:

dataEntity.ExecuteStoreCommand
   ("UPDATE items SET itemstatus = 'some status' WHERE id = 123 ");

ด้วยเหตุผลด้านประสิทธิภาพคุณอาจต้องการส่งผ่านตัวแปรแทนที่จะใช้สตริง SQL แบบฮาร์ดโค้ดเดียว สิ่งนี้จะช่วยให้ SQL Server แคชแบบสอบถามและใช้ซ้ำกับพารามิเตอร์ ตัวอย่าง:

dataEntity.ExecuteStoreCommand
   ("UPDATE items SET itemstatus = 'some status' WHERE id = {0}", new object[] { 123 });

UPDATE - สำหรับ EF 6.0

dataEntity.Database.ExecuteSqlCommand
       ("UPDATE items SET itemstatus = 'some status' WHERE id = {0}", new object[] { 123 });

10
ทำไมคุณถึงลดระดับคำตอบนี้โดยไม่แสดงความคิดเห็น ข้อเสนอแนะนี้กล่าวถึงคำถามของผู้เขียนต้นฉบับเฉพาะจุด
barrypicker

19
ExecuteStoreCommandไม่ใช่วิธี EF ในการทำเช่นนี้ แต่เพียงแค่ใช้สิ่งที่DbConnectionมีอยู่ภายในDbContextเพื่อดำเนินการคำสั่ง ไม่ใช่ฐานข้อมูลที่ไม่เชื่อเรื่องพระเจ้านับประสาอะไรกับการคงอยู่ของผู้ไม่เชื่อเรื่องพระเจ้า (เช่นตัวอย่างนี้จะผิดพลาดหาก OP เปลี่ยนเป็น XML)
just.another.programmer

10
@ just.another.programmer - ด้วยพลังอันยิ่งใหญ่มาพร้อมกับความรับผิดชอบที่ยิ่งใหญ่
barrypicker

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

5
@ BrainSlugs83 - ลองใช้ EF ข้ามเซิร์ฟเวอร์ลิงก์ที่รองรับ OpenQuery เท่านั้น - สนุกมากมาย บางครั้งคุณต้องใช้ SQL ดิบเพื่อให้งานสำเร็จ คุณไม่สามารถวาดโค้ดแยกกันเพื่อทำการทดสอบได้เสมอไป มันไม่ใช่โลกที่สมบูรณ์แบบ
barrypicker

22

รหัส:

ExampleEntity exampleEntity = dbcontext.ExampleEntities.Attach(new ExampleEntity { Id = 1 });
exampleEntity.ExampleProperty = "abc";
dbcontext.Entry<ExampleEntity>(exampleEntity).Property(ee => ee.ExampleProperty).IsModified = true;
dbcontext.Configuration.ValidateOnSaveEnabled = false;
dbcontext.SaveChanges();

ผลลัพธ์ TSQL:

exec sp_executesql N'UPDATE [dbo].[ExampleEntities]
SET [ExampleProperty ] = @0
WHERE ([Id] = @1)
',N'@0 nvarchar(32),@1 bigint',@0='abc',@1=1

บันทึก:

บรรทัด "IsModified = true" เป็นสิ่งจำเป็นเนื่องจากเมื่อคุณสร้างอ็อบเจ็กต์ ExampleEntity ใหม่ (เฉพาะที่มีการเติมคุณสมบัติ Id) คุณสมบัติอื่น ๆ ทั้งหมดจะมีค่าดีฟอลต์ (0, null ฯลฯ ) หากคุณต้องการอัปเดตฐานข้อมูลด้วย "ค่าเริ่มต้น" การเปลี่ยนแปลงจะไม่ถูกตรวจพบโดยกรอบงานเอนทิตีจากนั้น DB จะไม่ได้รับการอัพเดต

ในตัวอย่าง:

exampleEntity.ExampleProperty = null;

จะไม่ทำงานหากไม่มีบรรทัด "IsModified = true" เนื่องจากคุณสมบัติ ExampleProperty เป็นค่าว่างอยู่แล้วเมื่อคุณสร้างอ็อบเจ็กต์ ExampleEntity ที่ว่างเปล่าคุณต้องพูดกับ EF ว่าคอลัมน์นี้ต้องได้รับการอัปเดตและนี่คือจุดประสงค์ของบรรทัดนี้


เหมาะมากครับ ฉันเพิ่งทดสอบสิ่งนี้และเป็นสิ่งที่ฉันต้องการ ฉันต้องการให้การเปลี่ยนแปลงผ่านโครงสร้างพื้นฐาน EF (รวมถึงการใช้โปรเจ็กต์ EntityFramework.Triggers) แต่ต้องการเปลี่ยน 1 คอลัมน์โดยมีคีย์หลักเท่านั้น
MikeJansen

11

หากDataItemมีช่อง EF จะตรวจสอบความถูกต้องล่วงหน้า (เช่นช่องที่ไม่เป็นค่าว่าง) เราจะต้องปิดใช้งานการตรวจสอบความถูกต้องนั้นสำหรับบริบทนี้:

DataItem itemToUpdate = new DataItem { Id = id, Itemstatus = newStatus };
dataEntity.Entry(itemToUpdate).Property(x => x.Itemstatus).IsModified = true;
dataEntity.Configuration.ValidateOnSaveEnabled = false;
dataEntity.SaveChanges();
//dataEntity.Configuration.ValidateOnSaveEnabled = true;

มิฉะนั้นเราสามารถลองตอบสนองการตรวจสอบความถูกต้องล่วงหน้าและยังคงอัปเดตเฉพาะคอลัมน์เดียว:

DataItem itemToUpdate = new DataItem
{
    Id = id,
    Itemstatus = newStatus,
    NonNullableColumn = "this value is disregarded - the db original will remain"
};
dataEntity.Entry(itemToUpdate).Property(x => x.Itemstatus).IsModified = true;
dataEntity.SaveChanges();

สมมติว่าdataEntityเป็นSystem.Data.Entity.DbContext

คุณสามารถตรวจสอบการสืบค้นที่สร้างขึ้นโดยเพิ่มสิ่งนี้ในDbContext:

/*dataEntity.*/Database.Log = m => System.Diagnostics.Debug.Write(m);

2

บทความนี้เป็นส่วนหนึ่งของการเริ่มต้นใช้งานของMicrosoftอธิบายสถานะของเอนทิตีและวิธีการดำเนินการ:

เพิ่ม / แนบและสถานะของเอนทิตี

ดูส่วน 'การแนบเอนทิตีที่มีอยู่ แต่ถูกแก้ไขเข้ากับบริบท'

ตอนนี้ฉันออกไปอ่านบทแนะนำที่เหลือเหล่านี้


0

ทำงานแตกต่างกันบ้างใน EF Core:

อาจมีวิธีที่เร็วกว่าในการดำเนินการนี้ใน EF Core แต่วิธีต่อไปนี้ช่วยให้มั่นใจได้ว่าการอัปเดตโดยไม่ต้องทำการ SELECT (ทดสอบด้วย EF Core 2 และ JET บน. NET Framework 4.6.2):

ตรวจสอบให้แน่ใจว่าโมเดลของคุณไม่มีคุณสมบัติ IsRequired

จากนั้นใช้เทมเพลตต่อไปนี้ (ใน VB.NET):

    Using dbContext = new MyContext()
        Dim bewegung = dbContext.MyTable.Attach(New MyTable())
        bewegung.Entity.myKey = someKey
        bewegung.Entity.myOtherField = "1"

        dbContext.Entry(bewegung.Entity).State = EntityState.Modified
        dbContext.Update(bewegung.Entity)

        Dim BewegungenDescription = (From tp In dbContext.Model.GetEntityTypes() Where tp.ClrType.Name = "MyTable" Select tp).First()
        For Each p In (From prop In BewegungenDescription.GetProperties() Select prop)
            Dim pp = dbContext.Entry(bewegung.Entity).Property(p.Name)
            pp.IsModified = False
        Next
        dbContext.Entry(bewegung.Entity).Property(Function(row) row.myOtherField).IsModified = True
        dbContext.SaveChanges()
    End Using

-1

โดยทั่วไปถ้าคุณใช้ Entity Framework เพื่อสอบถามรายการทั้งหมดและคุณบันทึกวัตถุเอนทิตีคุณสามารถอัปเดตแต่ละรายการในวัตถุเอนทิตีและเรียกใช้SaveChanges()เมื่อคุณดำเนินการเสร็จสิ้น ตัวอย่างเช่น:

var items = dataEntity.Include("items").items;
// For each one you want to change:
items.First(item => item.id == theIdYouWant).itemstatus = newStatus;
// After all changes:
dataEntity.SaveChanges();

การดึงข้อมูลรายการเดียวที่คุณต้องการไม่ควรสร้างแบบสอบถามใหม่


คำตอบที่น่าสนใจมีใครยืนยันเรื่องนี้อีกหรือไม่?
เอียน

5
สิ่งนี้จะเหมือนกับปัญหาของ OP: ดึงข้อมูลทั้งหมดจากนั้นอัปเดต .First () deserializes วัตถุ
Jerther
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.