ฉันจะรับรหัสของเอนทิตีที่แทรกในกรอบงานของเอนทิตี้ได้อย่างไร [ปิด]


611

ฉันมีปัญหากับ Entity Framework ใน Asp.net ฉันต้องการรับค่ารหัสทุกครั้งที่ฉันเพิ่มวัตถุในฐานข้อมูล ฉันจะทำสิ่งนี้ได้อย่างไร


16
คำตอบของ Ladislav Mrnka ด้านล่างเป็นคำตอบที่ถูกต้องและควรได้รับการยอมรับเช่นนี้
CShark

3
OP ได้โพสต์เพียงครั้งเดียวด้วยบัญชี / เธอของเขาคำถามนี้จะเฉพาะเจาะจงมากขึ้น สิ่งนี้ทำให้เขา / เธอมีชื่อเสียงเกือบ 2k (!) คำตอบไม่ได้ถูกทำเครื่องหมายว่าเป็นที่ยอมรับเพราะบัญชีถูกยกเลิกตั้งแต่วันนั้น
MurariAlex

1
หากคุณต้องการ ID เพื่อใช้งานในความสัมพันธ์กับคีย์ต่างประเทศคุณอาจพิจารณาเพียงตั้งค่าคุณสมบัติการนำทางของเอนทิตีที่พึ่งพาของคุณเป็นเอนทิตีที่เพิ่มไว้ก่อนหน้านี้ วิธีนี้คุณไม่จำเป็นต้องกังวลเกี่ยวกับการโทรหาSaveChangesเพื่อรับ ID อ่านเพิ่มเติมที่นี่
B12Toaster

สำหรับ Entity Framework Core 3.0 ให้เพิ่มพารามิเตอร์ acceptAllChangesOnSuccess เป็น true: await _context.SaveChangesAsync (true);
Mike

คำตอบ:


1006

มันค่อนข้างง่าย หากคุณกำลังใช้ DB สร้างรหัส (เช่นIDENTITYใน MS SQL) คุณเพียงแค่ต้องเพิ่มนิติบุคคลObjectSetและบนที่เกี่ยวข้องSaveChanges จะถูกเติมโดยอัตโนมัติสำหรับคุณ:ObjectContextId

using (var context = new MyContext())
{
  context.MyEntities.Add(myNewObject);
  context.SaveChanges();

  int id = myNewObject.Id; // Yes it's here
}

เอนทิตีเฟรมเวิร์กตามค่าเริ่มต้นจะติดตามแต่ละรายการINSERTด้วยSELECT SCOPE_IDENTITY()เมื่อใช้Ids ที่สร้างขึ้นอัตโนมัติ


34
ดังนั้นคุณจะถามคำถามผิด หากคุณมีปัญหาเกี่ยวกับข้อยกเว้นคุณควรถามคำถามเพื่อแสดงข้อยกเว้นของคุณ (และข้อยกเว้นภายใน) และข้อมูลโค้ดทำให้เกิดข้อผิดพลาดนั้น
Ladislav Mrnka

3
ตรวจสอบให้แน่ใจว่าเอนทิตีที่คุณกำลังเพิ่มนั้นเป็นเอนทิตีที่ถูกต้องเช่นทุกอย่างที่มีข้อ จำกัด ของฐานข้อมูลที่เป็น "ไม่เป็นโมฆะ" ต้องถูกเติมเต็ม ฯลฯ
ฟราน

4
@LadislavMrnka: แล้วคุณสมบัติการนำทางของวัตถุที่สร้างขึ้นใหม่ ดูเหมือนว่าไม่ได้รับการเติมข้อมูลอัตโนมัติหลังจากบันทึกการเปลี่ยนแปลง
Isaac Kleinman

4
ลองพิจารณาสถานการณ์ต่อไปนี้: table1 ควรจะสร้าง @@ ข้อมูลประจำตัวเพื่อใช้ใน table2 ทั้ง table1 และ table2 ควรจะอยู่ในรายการเดียวกันสำหรับการดำเนินการ SaveChange หนึ่งการดำเนินการจะต้องบันทึกข้อมูลของทั้งสองตาราง @@ ข้อมูลประจำตัวจะไม่ถูกสร้างขึ้นเว้นแต่จะมีการเรียก 'SaveChanges' เราจะแก้ไขสถานการณ์นี้ได้อย่างไร
Arash

7
@Djeroen: ถ้าคุณใช้ฐานข้อมูลที่สร้าง Id คุณต้องใส่วัตถุเหล่านั้นลงในฐานข้อมูลก่อน หากคุณจำเป็นต้องมีรหัสก่อนการแทรกคุณต้องสร้างตรรกะของคุณเองเพื่อรับรหัสที่ไม่ซ้ำกันก่อนที่ข้อมูลจะถูกแทรกและไม่ใช้คอลัมน์ข้อมูลประจำตัวในฐานข้อมูล
Ladislav Mrnka

124

ฉันใช้คำตอบของ Ladislav Mrnkaเพื่อดึงรหัสสำเร็จเมื่อใช้ Entity Framework แต่ฉันกำลังโพสต์ที่นี่เพราะฉันพลาดการใช้งาน (เช่นใช้ที่ที่ไม่จำเป็น) และคิดว่าฉันจะโพสต์สิ่งที่ฉันค้นพบได้ที่นี่ - กรณีที่ผู้คนกำลังมองหา "แก้ปัญหา" ปัญหาที่ฉันมี

พิจารณาออเดอร์อ็อบเจกต์ที่มีความสัมพันธ์กับต่างประเทศกับลูกค้า เมื่อฉันเพิ่มลูกค้าใหม่และสั่งซื้อใหม่ในเวลาเดียวกันฉันทำอะไรเช่นนี้;

var customer = new Customer(); //no Id yet;
var order = new Order(); //requires Customer.Id to link it to customer;
context.Customers.Add(customer);
context.SaveChanges();//this generates the Id for customer
order.CustomerId = customer.Id;//finally I can set the Id

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

สิ่งที่ฉันต้องทำก็แค่นี้

var customer = new Customer(); //no Id yet;
var order = new Order{Customer = customer}; 
context.SaveChanges();//adds customer.Id to customer and the correct CustomerId to order

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

ฉันรู้ว่านี่ไม่ได้ตอบคำถามเดิม แต่คิดว่ามันอาจช่วยนักพัฒนาที่ยังใหม่กับ EF จากการใช้คำตอบที่ได้รับคะแนนสูงสุดสำหรับบางสิ่งที่อาจไม่จำเป็น

นอกจากนี้ยังหมายความว่าการอัปเดตเสร็จสมบูรณ์ในการทำธุรกรรมเดียวอาจหลีกเลี่ยงข้อมูล orphin (การอัปเดตทั้งหมดเสร็จสมบูรณ์หรือไม่มีเลย)


8
ขอบคุณมาก! เคล็ดลับการปฏิบัติที่ดีที่สุดที่สำคัญ: คำสั่ง var = คำสั่งซื้อใหม่ {ลูกค้า = ลูกค้า}; สิ่งนี้แสดงพลังของ Entity Framework ในการกำหนดวัตถุที่เกี่ยวข้องและรหัสจะได้รับการปรับปรุงโดยอัตโนมัติ
LA Guy 88

ขอบคุณสำหรับข้อมูลเพิ่มเติมนี้สำหรับคำตอบ มีประโยชน์มากในการบันทึก DB ไปกลับเมื่อใช้ EF
Prasad Korhale

ขอบคุณมากสำหรับเรื่องนี้. นี่คือสิ่งที่ฉันกำลังมองหา ฉันเชื่อว่าอาจมีวิธีที่ดีกว่าการเรียก "SaveChanges" สองครั้ง
Josh

1
โปรดระบุในคำตอบของคุณ (ควรใช้ตัวอักษรหนา) เพื่อแก้ไขพฤติกรรมการทำธุรกรรม หากวัตถุใดวัตถุหนึ่งไม่สามารถเพิ่มส่วนหนึ่งของธุรกรรมจะไม่สำเร็จ เช่นการเพิ่มบุคคลและนักเรียน บุคคลที่ประสบความสำเร็จและนักเรียนล้มเหลว ตอนนี้คนที่ซ้ำซ้อน ขอบคุณสำหรับวิธีแก้ปัญหาที่ยอดเยี่ยมนี้!
Imran Faruqi

32

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

db.Entry(MyNewObject).GetDatabaseValues();

แล้วก็

int id = myNewObject.Id;

ดู @jayantha ตอบคำถามด้านล่าง:

ฉันจะรับ Id ของเอนทิตีที่แทรกในเฟรมเวิร์กเอนทิตีเมื่อใช้ defaultValue ได้อย่างไร

การหาคำตอบ @christian ในคำถามด้านล่างอาจช่วยได้เช่นกัน:

Entity Framework Refresh บริบท


2
สวัสดีเมื่อฉันทำเช่นนี้ฉันได้รับข้อผิดพลาดในการคอมไพล์ซึ่งระบุว่า: ไม่สามารถแก้ไขคุณสมบัติเช่นรหัสหรือชื่อคุณสามารถช่วยฉันได้ไหม
Morteza Azizi

1
@MortezaAzizi ดูเหมือนว่าจะเป็นข้อผิดพลาดของการไม่จัดการหรือปัญหาทรัพย์สินที่เป็นโมฆะ แต่คุณช่วยกรุณาอธิบายเพิ่มเติมหรือเขียนบางส่วนของรหัส?
QMaster

3
ไม่ควรทำ.GetDatabaseValues();ถ้าคุณเพิ่งบันทึกโมเดลของคุณไปยังฐานข้อมูล ID ควร repopulate ให้กับโมเดลโดยอัตโนมัติ
vapcguy

3
@QMaster ยกเว้นว่า EF ยังสามารถ (และไม่) ดำเนินการคำสั่ง SQL ที่เหมือนกันมากและเติม ID ของวัตถุของคุณ มิฉะนั้นจะสามารถบันทึกหลายวัตถุในเวลาเดียวกันได้อย่างไร จะเป็นไปได้GetDatabaseValues()อย่างไรหากไม่ทราบ ID ของวัตถุที่ต้องการค้นหาในฐานข้อมูล
krillgar

2
@ vapcguy ฉันเห็น ขอบคุณสำหรับความต้องการของคุณ ฉันจะตรวจสอบว่าในทั้งสองเวอร์ชันของ EF ASAP
QMaster

16

โปรดอ้างอิงลิงค์นี้

http://www.ladislavmrnka.com/2011/03/the-bug-in-storegeneratedpattern-fixed-in-vs-2010-sp1/

คุณต้องตั้งค่าคุณสมบัติของ StoreGeneratedPattern เป็นตัวตนแล้วลองใช้รหัสของคุณเอง

หรือคุณสามารถใช้สิ่งนี้

using (var context = new MyContext())
{
  context.MyEntities.AddObject(myNewObject);
  context.SaveChanges();

  int id = myNewObject.Id; // Your Identity column ID
}

7
เช่นเดียวกับคำตอบที่ได้รับการโหวตสูงสุด
Lewis86

2
StoreGeneratedPatternเป็นชิ้นส่วนที่ขาดหายไปสำหรับฉัน
BurnsBA

1
นั่นเป็นวิธีที่จะไปเมื่อทำงานกับ ORACLE และ ON-Insert Trigger,
Karl

ลิงก์เสีย - โดเมนที่พัก
t.durden

สวัสดีด้วย Oracle ฉันต้องตั้งค่า StoreGeneratedPattern ใน Entity - ไปที่โปรแกรมดูรุ่นค้นหาตารางและ PK ของคุณเลือกคุณสมบัติ StoreGeneratedPattern และตั้งค่าเป็น Identity มันทำงานตามที่ระบุไว้ข้างต้นคำตอบ นี่เป็นกรณีที่คุณมีทริกเกอร์ที่เติม PK ของคุณจากลำดับการแทรก
Rob

15

วัตถุที่คุณกำลังบันทึกควรมีความถูกต้องIdหลังจากเผยแพร่การเปลี่ยนแปลงในฐานข้อมูล


27
คุณเคยเห็นข้อยกเว้นภายในหรือไม่ ;-)
Snowbear

6

คุณสามารถรับ ID ได้หลังจากบันทึก แต่คุณสามารถสร้าง Guid ใหม่และกำหนดก่อนบันทึกได้


4

ฉันเจอสถานการณ์ที่ฉันต้องการแทรกข้อมูลในฐานข้อมูล & ต้องการรหัสหลักโดยใช้กรอบงานเอนทิตี สารละลาย :

long id;
IGenericQueryRepository<myentityclass, Entityname> InfoBase = null;
try
 {
    InfoBase = new GenericQueryRepository<myentityclass, Entityname>();
    InfoBase.Add(generalinfo);
    InfoBase.Context.SaveChanges();
    id = entityclassobj.ID;
    return id;
 }

4

คำตอบทั้งหมดเหมาะสมมากสำหรับสถานการณ์ของตัวเองสิ่งที่ฉันทำแตกต่างกันคือฉันกำหนด int PK โดยตรงจากวัตถุ (TEntity) ที่ Add () กลับไปที่ตัวแปร int เช่นนี้

using (Entities entities = new Entities())
{
      int employeeId = entities.Employee.Add(new Employee
                        {
                            EmployeeName = employeeComplexModel.EmployeeName,
                            EmployeeCreatedDate = DateTime.Now,
                            EmployeeUpdatedDate = DateTime.Now,
                            EmployeeStatus = true
                        }).EmployeeId;

      //...use id for other work
}

ดังนั้นแทนที่จะสร้างวัตถุใหม่ทั้งหมดคุณเพียงแค่ทำสิ่งที่คุณต้องการ :)

แก้ไขสำหรับนาย @GertArnold:

ป้อนคำอธิบายรูปภาพที่นี่


3
มันไม่มีประโยชน์จริงๆที่จะเพิ่มวิธีที่ไม่เปิดเผยเพื่อกำหนดรหัส สิ่งนี้ไม่ได้เกี่ยวข้องกับ Entity Framework
Gert Arnold

1
@GertArnold .. ฉันมีคำถามเดียวกันกับ OP ฉันพบคำตอบและทำให้เป็นคำสั่งบรรทัดเดียวและฉันไม่เคยได้ยินคำว่าวิธีการที่ไม่เปิดเผยที่จะอธิบาย?
IteratioN7T

3
นั่นหมายความว่าคุณจะไม่แสดง (เปิดเผย) ทุกสิ่งที่คุณทำ บางทีมันอาจจะไม่ได้อธิบายอย่างชัดเจนฉันไม่รู้ สิ่งที่ฉันทำทราบว่าเป็นที่Add(ถ้าเป็นDbSet.Add) จะไม่ได้EmployeeIdอย่างน่าอัศจรรย์กำหนดค่าให้กับ
Gert Arnold

3
SaveChangesไม่ได้โดยไม่มี เป็นไปไม่ได้ ยกเว้นว่าคุณมีกระบวนการอื่น ๆ ที่กำหนดEmployeeIdตัวอย่างเช่นในตัวEmployeeสร้างของ หรือคุณใช้ EF core ForSqlServerUseSequenceHiLoอยู่ อย่างไรก็ตามคุณไม่ได้ให้ภาพรวมทั้งหมด
Gert Arnold

3
คำพูดสุดท้ายของฉันคือ: นี่ไม่ใช่พฤติกรรมของ EF มาตรฐาน คุณควรให้รายละเอียดเพิ่มเติมเกี่ยวกับสภาพแวดล้อมของคุณ รุ่น EF แบรนด์ฐานข้อมูลและรุ่นคลาสพนักงานรายละเอียดการแมป
Gert Arnold

3
Repository.addorupdate(entity, entity.id);
Repository.savechanges();
Var id = entity.id;

สิ่งนี้จะได้ผล


4
พื้นที่เก็บข้อมูลจะไม่รับผิดชอบในการบันทึกการเปลี่ยนแปลงลงในฐานข้อมูล มันเป็นงานของ UnitOfWork
tchelidze

5
ยิ่งไปกว่านั้นมันไม่ชัดเจนว่าRepositoryเป็นอะไร และ "สิ่งนี้จะได้ผล" เป็นคำอธิบาย!
Gert Arnold

2

มีสองกลยุทธ์:

  1. ใช้ฐานข้อมูลที่สร้างID( intหรือGUID)

    จุดด้อย:

    คุณควรดำเนินการSaveChanges()เพื่อรับIDหน่วยงานที่บันทึกไว้เพียงอย่างเดียว

    ข้อดี:

    สามารถใช้intตัวตน

  2. ใช้ไคลเอ็นต์ที่สร้างID- GUID เท่านั้น

    ข้อดี: การลดขนาดของSaveChangesการดำเนินการ สามารถแทรกกราฟขนาดใหญ่ของวัตถุใหม่ต่อหนึ่งการดำเนินการ

    จุดด้อย:

    อนุญาตเฉพาะสำหรับ GUID


1

เมื่อคุณใช้รหัส EF 6.x ก่อน

    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public Guid Id { get; set; }

และเริ่มต้นตารางฐานข้อมูลก็จะใส่

(newsequentialid())

ภายในคุณสมบัติตารางภายใต้ส่วนหัวค่าเริ่มต้นหรือการผูกช่วยให้ ID ที่จะเติมเมื่อมันถูกแทรก

ปัญหาคือถ้าคุณสร้างตารางและเพิ่ม

[DatabaseGenerated(DatabaseGeneratedOption.Identity)]

ส่วนต่อมาฐานข้อมูลการอัพเดทในอนาคตจะไม่เพิ่มกลับ (newsequentialid ())

ในการแก้ไขวิธีที่เหมาะสมคือการลบข้อมูลการย้ายให้ลบฐานข้อมูลและย้ายอีกครั้ง ... หรือคุณสามารถเพิ่ม (newsequentialid ()) ลงในตัวออกแบบตาราง


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

1
@josh สมมติว่ารหัสแรกคือประเภท Guid คลิกขวาที่ตารางของคุณใน Sql Server Management Studio คลิกคอลัมน์รหัสและใต้แท็บคุณสมบัติคอลัมน์ด้านใน (ทั่วไป) คุณจะเห็นค่าเริ่มต้นหรือผูกพัน ถ้าคุณกำลังสร้างตารางฐานข้อมูลด้วยมือหมายถึงNewSequentialIdหรือnewid กรณีการใช้งานทั่วไปเป็นสิ่งที่ต้องการwhen -column- is NULL, then NEWID()
Jeff Li
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.