เป็นไปได้ที่จะตรวจสอบว่าอ็อบเจ็กต์ถูกแนบกับบริบทข้อมูลใน Entity Framework แล้วหรือไม่


87

ฉันได้รับข้อผิดพลาดต่อไปนี้เมื่อพยายามแนบวัตถุที่แนบกับบริบทที่กำหนดผ่านcontext.AttachTo(...):

มีวัตถุที่มีคีย์เดียวกันอยู่แล้วใน ObjectStateManager ObjectStateManager ไม่สามารถติดตามวัตถุหลายชิ้นด้วยคีย์เดียวกัน

มีวิธีในการบรรลุบางสิ่งตามแนวของ:

context.IsAttachedTo(...)

ไชโย!

แก้ไข:

วิธีการขยายที่ Jason สรุปไว้นั้นใกล้เคียง แต่ใช้ไม่ได้กับสถานการณ์ของฉัน

ฉันกำลังพยายามทำงานบางอย่างโดยใช้วิธีการที่ระบุไว้ในคำตอบสำหรับคำถามอื่น:

ฉันจะลบแถวอย่างน้อยหนึ่งแถวออกจากตารางของฉันโดยใช้ Linq ไปยัง Entities * โดยไม่ * ดึงข้อมูลแถวก่อนได้อย่างไร

รหัสของฉันมีลักษณะดังนี้:

var user = new User() { Id = 1 };
context.AttachTo("Users", user);
comment.User = user;
context.SaveChanges();

วิธีนี้ใช้ได้ดียกเว้นเมื่อฉันทำอย่างอื่นให้กับผู้ใช้รายนั้นโดยที่ฉันใช้วิธีการเดียวกันและพยายามแนบUserวัตถุจำลอง สิ่งนี้ล้มเหลวเนื่องจากฉันได้แนบวัตถุผู้ใช้จำลองนั้นไว้ก่อนหน้านี้ ฉันจะตรวจสอบสิ่งนี้ได้อย่างไร?

คำตอบ:


57

นี่คือสิ่งที่ฉันลงเอยด้วยซึ่งใช้งานได้ดีมาก:

public static void AttachToOrGet<T>(this ObjectContext context, string entitySetName, ref T entity)
    where T : IEntityWithKey
{
    ObjectStateEntry entry;
    // Track whether we need to perform an attach
    bool attach = false;
    if (
        context.ObjectStateManager.TryGetObjectStateEntry
            (
                context.CreateEntityKey(entitySetName, entity),
                out entry
            )
        )
    {
        // Re-attach if necessary
        attach = entry.State == EntityState.Detached;
        // Get the discovered entity to the ref
        entity = (T)entry.Entity;
    }
    else
    {
        // Attach for the first time
        attach = true;
    }
    if (attach)
        context.AttachTo(entitySetName, entity);
}

คุณสามารถเรียกมันได้ดังนี้:

User user = new User() { Id = 1 };
II.AttachToOrGet<Users>("Users", ref user);

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


ฉันมีปัญหาที่คล้ายกันและสิ่งนี้ก็ช่วยแก้ปัญหาของฉันได้ - ยอดเยี่ยมไชโย! +1
RPM1984

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

1
มีความคิดว่าทำไม(T)entry.Entityบางครั้งจึงคืนค่าว่าง
Tr1stan

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

1
ถ้าTมีอยู่แล้วIEntityWithKeyคุณไม่สามารถใช้entity.EntityKeyคุณสมบัติแทนที่จะสร้างขึ้นใหม่หรือต้องการคาดเดา / จัดหาEntitySetName?
drzaus

55

แนวทางที่ง่ายกว่าคือ:

 bool isDetached = context.Entry(user).State == EntityState.Detached;
 if (isDetached)
     context.Users.Attach(user);

1
สิ่งนี้ใช้ได้ผลสำหรับฉันเพียงแค่ฉันต้องใช้ "EntityState.Detached" แทนที่จะเป็นเพียง "แยก" ...
Marcelo Myara

22
อืมลองวิธีแก้ปัญหาของคุณแล้ว แต่สำหรับฉัน isDetached เป็นจริง แต่ยังคงเป็นข้อผิดพลาดเดียวกันเมื่อฉันพยายามแนบรายการไปยังบริบท
Prokurors

การประเมินที่ยอดเยี่ยม แม้ว่าจะใช้งานได้กับ Generic Repository ของฉัน
ATHER

5
@Prokurors ฉันเพิ่งได้เรียนรู้เหตุผลที่เช็ค Mosh คือไม่เพียงพอที่จะป้องกันไม่ให้เกิดข้อผิดพลาดอยู่เสมอก็คือว่า.Include()'คุณสมบัตินำทางเอ็ดนอกจากนี้ยังมีความพยายามที่จะแนบมาเมื่อคุณเรียก.Attachหรือการตั้งค่าของมันEntityStateไปEntityState.Unchanged- และพวกเขาจะขัดแย้งถ้าใด ๆ ของหน่วยงานที่อ้างถึง นิติบุคคลเดียวกัน ฉันยังไม่ทราบวิธีการแนบเฉพาะเอนทิตีพื้นฐานดังนั้นฉันจึงต้องออกแบบโครงการใหม่เล็กน้อยเพื่อใช้บริบทแยกกันสำหรับ "ธุรกรรมทางธุรกิจ" แต่ละรายการเหมือนที่ออกแบบมา ฉันจะจดบันทึกไว้สำหรับโครงการในอนาคต
Aske B.

18

ลองใช้วิธีการขยายนี้ (ยังไม่ได้ทดสอบและปิดข้อมือ):

public static bool IsAttachedTo(this ObjectContext context, object entity) {
    if(entity == null) {
        throw new ArgumentNullException("entity");
    }
    ObjectStateEntry entry;
    if(context.ObjectStateManager.TryGetObjectStateEntry(entity, out entry)) {
        return (entry.State != EntityState.Detached);
    }
    return false;
}

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

public static bool IsAttachedTo(this ObjectContext, EntityKey key) {
    if(key == null) {
        throw new ArgumentNullException("key");
    }
    ObjectStateEntry entry;
    if(context.ObjectStateManager.TryGetObjectStateEntry(key, out entry)) {
        return (entry.State != EntityState.Detached);
    }
    return false;
}

ในการสร้างEntityKeyสถานการณ์ของคุณให้ใช้สิ่งต่อไปนี้เป็นแนวทาง:

EntityKey key = new EntityKey("MyEntities.User", "Id", 1);

คุณสามารถรับEntityKeyอินสแตนซ์ที่มีอยู่ได้Userโดยใช้คุณสมบัติUser.EntityKey(จากอินเทอร์เฟซIEntityWithKey)


นี่เป็นสิ่งที่ดีมาก แต่มันไม่ได้ผลสำหรับฉันในสถานการณ์ของฉัน ... ฉันจะอัปเดตคำถามพร้อมรายละเอียด ps คุณต้องการให้บูลไม่ใช่บูลีนและคงที่ แต่นอกเหนือจากวิธีการขยายที่ยอดเยี่ยม!
joshcomley

@joshcomley: ฉันคิดว่าคุณสามารถพูดโดยใช้การโอเวอร์โหลดTryGetObjectStateEntryที่ยอมรับEntityKeyแทนobjectไฟล์. ฉันได้แก้ไขตามนั้น โปรดแจ้งให้เราทราบหากวิธีนี้ไม่ได้ผลเราจะกลับไปที่กระดานวาดภาพ
jason

อาเพิ่งเห็นสิ่งนี้ - ฉันกำลังหาวิธีแก้ปัญหาที่ระบุไว้ในคำตอบที่เพิ่งโพสต์ +1 สำหรับความช่วยเหลือและคำแนะนำของคุณ !!
joshcomley

มีความคิดว่าทำไมฉันถึงได้รับข้อผิดพลาดรายละเอียดอยู่ที่นี่stackoverflow.com/questions/6653050/…
Joper


0

นี่ไม่ได้ตอบคำถาม OPs โดยตรง แต่นี่คือวิธีที่ฉันแก้ไขของฉัน

นี้สำหรับผู้ที่ใช้แทนDbContextObjectContext

    public TEntity Retrieve(object primaryKey)
    {
        return DbSet.Find(primaryKey);
    }

DbSet.Find วิธีการ :

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

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

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.