मैं एंटिटी फ्रेमवर्क को बाल वस्तुओं को बचाने / सम्मिलित करने की कोशिश से कैसे रोक सकता हूं?


100

जब मैं इकाई ढांचे के साथ एक इकाई को बचाता हूं, तो मैंने स्वाभाविक रूप से यह माना कि यह केवल निर्दिष्ट इकाई को बचाने की कोशिश करेगा। हालाँकि, यह उस संस्था की बाल संस्थाओं को बचाने की भी कोशिश कर रहा है। यह सभी प्रकार की अखंडता समस्याओं का कारण बन रहा है। मैं ईएफ को केवल उस इकाई को बचाने के लिए मजबूर करता हूं जिसे मैं बचाना चाहता हूं और इसलिए सभी बाल वस्तुओं को अनदेखा करता हूं?

यदि मैं मैन्युअल रूप से गुणों को शून्य करने के लिए सेट करता हूं, तो मुझे एक त्रुटि मिलती है "ऑपरेशन विफल रहा: संबंध को नहीं बदला जा सका क्योंकि विदेशी-कुंजी गुणों में से एक या अधिक गैर-अशक्त है।" यह बेहद प्रति-उत्पादक है क्योंकि मैंने बच्चे के ऑब्जेक्ट को विशेष रूप से अशक्त करने के लिए सेट किया है इसलिए ईएफ इसे अकेला छोड़ देगा।

मैं बच्चे की वस्तुओं को सहेजना / डालना क्यों नहीं चाहता?

चूंकि इस पर टिप्पणियों में आगे और पीछे चर्चा की जा रही है, मैं कुछ औचित्य बताऊंगा कि मैं अपने बच्चे की वस्तुओं को अकेला क्यों छोड़ना चाहता हूं।

मैं जिस एप्लिकेशन का निर्माण कर रहा हूं, उसमें EF ऑब्जेक्ट मॉडल डेटाबेस से लोड नहीं किया जा रहा है, बल्कि डेटा ऑब्जेक्ट के रूप में उपयोग किया जाता है, जिसे मैं एक फ्लैट फ़ाइल पार्स करते समय पॉपुलेट कर रहा हूं। बाल वस्तुओं के मामले में, इनमें से कई माता-पिता की तालिका के विभिन्न गुणों को परिभाषित करने वाली तालिकाओं को संदर्भित करते हैं। उदाहरण के लिए, प्राथमिक इकाई की भौगोलिक स्थिति।

चूंकि मैंने इन वस्तुओं को खुद ही आबाद किया है, इसलिए ईएफ मानता है कि ये नई वस्तुएं हैं और इन्हें मूल वस्तु के साथ सम्मिलित करने की आवश्यकता है। हालाँकि, ये परिभाषाएँ पहले से मौजूद हैं और मैं डेटाबेस में डुप्लिकेट बनाना नहीं चाहता। मैं केवल EF ऑब्जेक्ट का उपयोग लुकअप करने के लिए करता हूं और अपनी मुख्य टेबल इकाई में विदेशी कुंजी को पॉप्युलेट करता हूं।

यहां तक ​​कि बच्चे की वस्तुओं के साथ जो वास्तविक डेटा हैं, मुझे पहले माता-पिता को बचाने और प्राथमिक कुंजी प्राप्त करने की आवश्यकता है या ईएफ केवल चीजों की गड़बड़ी करने के लिए लगता है। आशा है कि यह कुछ स्पष्टीकरण देता है।


जहां तक ​​मुझे पता है कि आपको बाल वस्तुओं को शून्य करना होगा।
जोहान

हाय जोहान। काम नहीं करता है। यदि मैं संग्रह को शून्य करता हूं, तो यह त्रुटियां फेंकता है। मैं इसे कैसे करता हूं, इसके आधार पर, यह कुंजियों के अशक्त होने की शिकायत करता है या कि मैं संग्रह को संशोधित कर चुका हूं। जाहिर है, वे बातें सच हैं, लेकिन मैंने ऐसा इसलिए किया ताकि यह उन वस्तुओं को छोड़ दे, जिन्हें छूने की जरूरत नहीं है।
मार्क Micallef

यूफोरिक, यह पूरी तरह से अनपेक्षित है।
मार्क Micallef

@ उत्साही बाल वस्तुओं को न बदलने पर भी, EF अभी भी उन्हें डिफ़ॉल्ट रूप से सम्मिलित करने का प्रयास करता है और उन्हें अनदेखा नहीं करता है या उन्हें अपडेट नहीं करता है।
जोहान

क्या वास्तव में मुझे गुस्सा दिलाता है कि अगर मैं अपने रास्ते से हटकर वास्तव में उन वस्तुओं को अशक्त करता हूं, तो यह एहसास होने के बजाय शिकायत करता है कि मैं चाहता हूं कि यह उन्हें अकेला छोड़ दे। चूंकि उन चाइल्ड ऑब्जेक्ट्स सभी वैकल्पिक (डेटाबेस में अशक्त) हैं, क्या ईएफ को यह बताने के लिए मजबूर करने का कोई तरीका है कि मेरे पास उन ऑब्जेक्ट थे? इसके संदर्भ या कैश किसी तरह शुद्ध?
मार्क Micallef

जवाबों:


55

जहाँ तक मुझे पता है, आपके पास दो विकल्प हैं।

विकल्प 1)

सभी बाल वस्तुओं को बंद कर दें, इससे ईएफ को कुछ भी नहीं जोड़ना सुनिश्चित होगा। यह आपके डेटाबेस से कुछ भी नहीं हटाएगा।

विकल्प 2)

निम्नलिखित कोड का उपयोग करते हुए बच्चे की वस्तुओं को संदर्भ से अलग कर दें

 context.Entry(yourObject).State = EntityState.Detached

ध्यान दें कि आप List/ a को अलग नहीं कर सकते Collection। आपको अपनी सूची पर लूप करना होगा और अपनी सूची में प्रत्येक आइटम को अलग करना होगा

foreach (var item in properties)
{
     db.Entry(item).State = EntityState.Detached;
}

हाय जोहान, मैंने संग्रह में से एक को अलग करने की कोशिश की और यह निम्नलिखित त्रुटि थी: इकाई प्रकार HashSet`1 वर्तमान संदर्भ के लिए मॉडल का हिस्सा नहीं है।
मार्क मिकलिफ़

@MarkyMark संग्रह को अलग न करें। आपको संग्रह पर लूप करना होगा और ऑब्जेक्ट के लिए इसे अलग करना होगा (मैं अब अपना उत्तर अपडेट करूंगा)।
जोहान

11
दुर्भाग्य से, सब कुछ अलग करने के लिए छोरों की एक सूची के साथ, EF अभी भी संबंधित तालिकाओं में से कुछ में डालने की कोशिश कर रहा है। इस बिंदु पर, मैं EF को चीर कर SQL में वापस जाने के लिए तैयार हूं जो कम से कम समझदारी से व्यवहार करता है। क्या मुसीबत है।
मार्क मिकलिफ़

2
क्या मैं संदर्भ का उपयोग कर सकता / सकती हूं (अपना विकल्प)। इसे जोड़ने से पहले भी।
थॉमस क्लैमर

1
@ mirind4 मैंने राज्य का उपयोग नहीं किया। यदि मैं कोई ऑब्जेक्ट सम्मिलित करता हूं, तो मुझे यकीन है कि सभी चिल्ड अशक्त हैं। अद्यतन करने पर मुझे पहले वस्तु बिना चिल्ड के मिलती है।
थॉमस क्लैमर

41

लंबी कहानी छोटी: विदेशी कुंजी का उपयोग करें और यह आपके दिन को बचाएगा।

मान लें कि आपके पास एक स्कूल इकाई और एक शहर है इकाई है, और यह कई-से-एक संबंध है जहां एक शहर में कई स्कूल हैं और एक स्कूल एक शहर से संबंधित है। और मान लें कि शहर पहले से ही लुकअप टेबल में मौजूद हैं, इसलिए आप नहीं चाहते कि नया स्कूल डालते समय उन्हें फिर से डाला जाए।

प्रारंभ में आप इस तरह से संस्थाओं को परिभाषित कर सकते हैं:

public class City
{
    public int Id { get; set; }
    public string Name { get; set; }
}

public class School
{
    public int Id { get; set; }
    public string Name { get; set; }

    [Required]
    public City City { get; set; }
}

और आप इस तरह से स्कूल प्रविष्टि कर सकते हैं (मान लें कि आपके पास पहले से ही शहर की संपत्ति newItem को सौंपी गई है ):

public School Insert(School newItem)
{
    using (var context = new DatabaseContext())
    {
        context.Set<School>().Add(newItem);
        // use the following statement so that City won't be inserted
        context.Entry(newItem.City).State = EntityState.Unchanged;
        context.SaveChanges();
        return newItem;
    }
}

उपरोक्त दृष्टिकोण इस मामले में पूरी तरह से काम कर सकता है, हालांकि, मुझे विदेशी कुंजी दृष्टिकोण पसंद है जो मेरे लिए अधिक स्पष्ट और लचीला है। नीचे अद्यतन समाधान देखें:

public class City
{
    public int Id { get; set; }
    public string Name { get; set; }
}

public class School
{
    public int Id { get; set; }
    public string Name { get; set; }

    [ForeignKey("City_Id")]
    public City City { get; set; }

    [Required]
    public int City_Id { get; set; }
}

इस तरह, आप स्पष्ट रूप से परिभाषित करते हैं कि स्कूल में एक विदेशी कुंजी City_Id है और यह सिटी इकाई को संदर्भित करता है । इसलिए जब स्कूल की प्रविष्टि की बात आती है , तो आप यह कर सकते हैं:

    public School Insert(School newItem, int cityId)
    {
        if(cityId <= 0)
        {
            throw new Exception("City ID no provided");
        }

        newItem.City = null;
        newItem.City_Id = cityId;

        using (var context = new DatabaseContext())
        {
            context.Set<School>().Add(newItem);
            context.SaveChanges();
            return newItem;
        }
    }

इस स्थिति में, आप स्पष्ट रूप से नए रिकॉर्ड के City_Id को निर्दिष्ट करते हैं और शहर को ग्राफ़ से हटाते हैं ताकि EF इसे स्कूल के साथ संदर्भ में जोड़ने के लिए परेशान न करे ।

हालांकि पहली छाप में विदेशी कुंजी दृष्टिकोण अधिक जटिल लगता है, लेकिन मुझ पर विश्वास करें यह मानसिकता आपको बहुत समय बचाएगी जब कई-कई संबंधों को सम्मिलित करने की बात आती है (आपके पास एक स्कूल और छात्र संबंध है, और छात्र एक शहर की संपत्ति है) और इतने पर।

यह आप के लिए उपयोगी है उम्मीद है।


महान जवाब, मेरी बहुत मदद की! लेकिन क्या [Range(1, int.MaxValue)]विशेषता का उपयोग करके City_Id के लिए 1 का न्यूनतम मूल्य बताना बेहतर नहीं होगा ?
डेन रेसन

एक सपने की तरह!! बहुत - बहुत धन्यवाद!
CJH

यह कॉलर में स्कूल ऑब्जेक्ट से मान को निकाल देगा। SaveChanges null नेविगेशन प्रॉपर्टीज की वैल्यू को फिर से लोड करता है? अन्यथा कॉल करने वाले को इन्सर्ट () विधि को कॉल करने के बाद सिटी ऑब्जेक्ट को फिर से लोड करना चाहिए यदि उसे उस जानकारी की आवश्यकता है। यह वह पैटर्न है जिसका मैंने अक्सर उपयोग किया है, लेकिन मैं अभी भी बेहतर पैटर्न के लिए खुला हूं अगर किसी के पास अच्छा है।
यह उतार चढ़ाव भरा

1
यह वास्तव में मेरे लिए मददगार था। EntityState.Unchaged बिल्कुल वही है जो मुझे किसी ऑब्जेक्ट को असाइन करने के लिए आवश्यक था जो लुकअप टेबल विदेशी कुंजी को एक बड़े ऑब्जेक्ट ग्राफ़ में प्रस्तुत करता है जिसे मैं एकल लेनदेन के रूप में सहेज रहा था। EF Core सहज ज्ञान युक्त त्रुटि संदेश IMO से कम फेंकता है। मैंने अपने लुकअप टेबल्स को कैश किया जो प्रदर्शन कारणों से शायद ही कभी बदलते हैं। आप अनुमान लगा रहे हैं, क्योंकि लुकअप टेबल ऑब्जेक्ट का पीके वही है जो डेटाबेस में पहले से ही है कि वह अपने अपरिवर्तित को जानता है और मौजूदा आइटम के लिए केवल एफके को असाइन करता है। इसके बजाय लुकअप टेबल ऑब्जेक्ट को नए के रूप में सम्मिलित करने का प्रयास करता है।
tnk479

राज्य को अपरिवर्तित करने के लिए अशक्त करने या स्थापित करने का कोई संयोजन मेरे लिए काम नहीं कर रहा है। EF जोर देकर कहता है कि मुझे एक नया चाइल्ड रिकॉर्ड डालना होगा जब मैं सिर्फ पैरेंट में एक स्केलर फील्ड अपडेट करना चाहता हूं।
BobRz

21

यदि आप केवल किसी मूल वस्तु में परिवर्तन संग्रहीत करना चाहते हैं और उसके किसी भी बच्चे की वस्तुओं में परिवर्तन करने से बचना चाहते हैं, तो केवल निम्न क्यों नहीं करें:

using (var ctx = new MyContext())
{
    ctx.Parents.Attach(parent);
    ctx.Entry(parent).State = EntityState.Added;  // or EntityState.Modified
    ctx.SaveChanges();
}

पहली पंक्ति माता-पिता की वस्तु और उसके आश्रित बच्चे की वस्तुओं के पूरे ग्राफ को Unchangedराज्य में संदर्भ में संलग्न करती है ।

दूसरी पंक्ति राज्य को केवल मूल वस्तु के लिए बदलती है, राज्य में अपने बच्चों को छोड़ देती Unchangedहै।

ध्यान दें कि मैं एक नए बनाए गए संदर्भ का उपयोग करता हूं, इसलिए यह डेटाबेस में किसी अन्य परिवर्तन को सहेजने से बचता है।


2
इस उत्तर का यह लाभ है कि यदि कोई बाद में साथ आता है और कोई चाइल्ड ऑब्जेक्ट जोड़ता है, तो यह मौजूदा कोड को नहीं तोड़ेगा। यह एक "ऑप्ट इन" समाधान है जहां दूसरों को स्पष्ट रूप से बाल वस्तुओं को बाहर करने की आवश्यकता होती है।
जिम

यह अब कोर में काम नहीं करता है। "संलग्न करें: प्रत्येक पहुंच योग्य इकाई को संलग्न करता है, जहां एक पहुंच योग्य इकाई के पास स्टोर-जनरेट की गई कुंजी होती है और कोई कुंजी मान असाइन नहीं किया जाता है; इन्हें जोड़ा जाएगा।" यदि बच्चे नए हैं तो उन्हें भी जोड़ा जाएगा।
mmix

14

सुझाए गए समाधानों में से एक नेविगेशन डेटाबेस को उसी डेटाबेस संदर्भ से असाइन करना है। इस समाधान में, डेटाबेस संदर्भ के बाहर से सौंपी गई नेविगेशन संपत्ति को प्रतिस्थापित किया जाएगा। कृपया, उदाहरण के लिए उदाहरण देखें।

class Company{
    public int Id{get;set;}
    public Virtual Department department{get; set;}
}
class Department{
    public int Id{get; set;}
    public String Name{get; set;}
}

डेटाबेस में सहेजना:

 Company company = new Company();
 company.department = new Department(){Id = 45}; 
 //an Department object with Id = 45 exists in database.    

 using(CompanyContext db = new CompanyContext()){
      Department department = db.Departments.Find(company.department.Id);
      company.department = department;
      db.Companies.Add(company);
      db.SaveChanges();
  }

Microsoft इसे एक सुविधा के रूप में बताता है, हालाँकि मुझे यह कष्टप्रद लगता है। यदि कंपनी ऑब्जेक्ट से संबंधित विभाग ऑब्जेक्ट में Id है जो पहले से डेटाबेस में मौजूद है, तो EF डेटाबेस ऑब्जेक्ट के साथ कंपनी ऑब्जेक्ट को संबद्ध क्यों नहीं करता है? हमें खुद से संघ की देखभाल करने की आवश्यकता क्यों है? नई वस्तु को जोड़ने के दौरान नेविगेशन संपत्ति का ख्याल रखना कुछ ऐसा है जैसे कि डेटाबेस ऑपरेशंस को SQL से C # में ले जाना, डेवलपर्स के लिए बोझिल।


मैं मानता हूं कि 100% यह हास्यास्पद है कि आईडी प्रदान करने पर ईएफ एक नया रिकॉर्ड बनाने का प्रयास करता है। यदि व्यापार में हमारे पास वास्तविक वस्तु के साथ चयन सूची विकल्प भरने का एक तरीका था।
T3.0

10

पहले आपको यह जानना होगा कि ईएफ में इकाई को अपडेट करने के दो तरीके हैं।

  • जुड़ी हुई वस्तुएँ

जब आप ऊपर वर्णित विधियों में से एक का उपयोग करके ऑब्जेक्ट संदर्भ से जुड़ी वस्तुओं के संबंध को बदलते हैं, तो एंटिटी फ्रेमवर्क को विदेशी कुंजी, संदर्भ और संग्रह को सिंक में रखने की आवश्यकता होती है।

  • तिरस्कृत वस्तु

यदि आप डिस्कनेक्ट की गई वस्तुओं के साथ काम कर रहे हैं, तो आपको मैन्युअल रूप से सिंक्रनाइज़ेशन का प्रबंधन करना होगा।

मैं जिस एप्लिकेशन का निर्माण कर रहा हूं, उसमें EF ऑब्जेक्ट मॉडल को डेटाबेस से लोड नहीं किया जा रहा है, बल्कि डेटा ऑब्जेक्ट के रूप में उपयोग किया जाता है, जिसे मैं एक फ्लैट फ़ाइल को पार्स करते समय पॉपुलेट कर रहा हूं।

इसका मतलब है कि आप डिस्कनेक्ट किए गए ऑब्जेक्ट के साथ काम कर रहे हैं, लेकिन यह स्पष्ट नहीं है कि आप स्वतंत्र एसोसिएशन या विदेशी कुंजी एसोसिएशन का उपयोग कर रहे हैं या नहीं

  • जोड़ना

    मौजूदा चाइल्ड ऑब्जेक्ट (ऑब्जेक्ट जो डेटाबेस में मौजूद है) के साथ नई इकाई जोड़ते समय, यदि चाइल्ड ऑब्जेक्ट ईएफ द्वारा ट्रैक नहीं किया जाता है, तो चाइल्ड ऑब्जेक्ट फिर से डाला जाएगा। जब तक आप मैन्युअल रूप से चाइल्ड ऑब्जेक्ट को पहले संलग्न नहीं करते हैं।

      db.Entity(entity.ChildObject).State = EntityState.Modified;
      db.Entity(entity).State = EntityState.Added;
  • अपडेट करें

    आप इकाई को केवल संशोधित के रूप में चिह्नित कर सकते हैं, फिर सभी स्केलर गुणों को अपडेट किया जाएगा और नेविगेशन गुणों को केवल अनदेखा किया जाएगा।

      db.Entity(entity).State = EntityState.Modified;

ग्राफ डिफ

यदि आप डिस्कनेक्ट की गई ऑब्जेक्ट के साथ काम करते समय कोड को सरल बनाना चाहते हैं, तो आप ग्राफ डिफाइन लाइब्रेरी को एक कोशिश दे सकते हैं

यहाँ परिचय है, एंटिटी फ्रेमवर्क कोड फर्स्ट के लिए ग्राफडिफ़ प्रस्तुत करना - अलग-अलग संस्थाओं के ग्राफ के स्वचालित अपडेट की अनुमति देना

नमूना कोड

  • यदि मौजूद नहीं है तो इकाई डालें, अन्यथा अपडेट करें।

      db.UpdateGraph(entity);
  • यदि यह मौजूद नहीं है तो इकाई डालें, अन्यथा अद्यतन करें और यदि यह मौजूद नहीं है तो बच्चे की वस्तु डालें, अन्यथा अद्यतन करें।

      db.UpdateGraph(entity, map => map.OwnedEntity(x => x.ChildObject));

3

ऐसा करने का सबसे अच्छा तरीका है कि आप अपने डेटाकोटेक्स्ट में SaveChanges फ़ंक्शन को ओवरराइड कर सकते हैं।

    public override int SaveChanges()
    {
        var added = this.ChangeTracker.Entries().Where(e => e.State == System.Data.EntityState.Added);

        // Do your thing, like changing the state to detached
        return base.SaveChanges();
    }

2

मुझे एक ही समस्या है जब मैं प्रोफ़ाइल को बचाने की कोशिश कर रहा हूं, तो मैं पहले से ही प्रोफ़ाइल बनाने के लिए नमस्कार और नया हूं। जब मैं प्रोफ़ाइल सम्मिलित करता हूँ तो यह अभिवादन में सम्मिलित होता है। तो मैंने इस तरह की बचत करने से पहले () की कोशिश की।

db.Entry (Profile.Salutation) .State = EntityState.Unchanged;


1

यह मेरे लिए काम किया:

// temporarily 'detach' the child entity/collection to have EF not attempting to handle them
var temp = entity.ChildCollection;
entity.ChildCollection = new HashSet<collectionType>();

.... do other stuff

context.SaveChanges();

entity.ChildCollection = temp;

0

हमने जो कुछ किया है, वह माता-पिता को डिस्बेट में जोड़ने से पहले, माता-पिता से बच्चे के संग्रह को डिस्कनेक्ट करें, मौजूदा संग्रह को अन्य चर को बाद में उनके साथ काम करने की अनुमति देने के लिए सुनिश्चित करें, और फिर नए खाली संग्रह के साथ वर्तमान बाल संग्रह की जगह लें। अशक्त / कुछ भी नहीं करने के लिए बच्चे के संग्रह की स्थापना हमारे लिए विफल रही। ऐसा करने के बाद माता-पिता को डबसेट में शामिल करें। इस तरह से बच्चों को तब तक नहीं जोड़ा जाता है जब तक आप उन्हें चाहते हैं।


0

मुझे पता है कि यह पुरानी पोस्ट है लेकिन यदि आप कोड-प्रथम दृष्टिकोण का उपयोग कर रहे हैं तो आप अपनी मैपिंग फ़ाइल में निम्न कोड का उपयोग करके वांछित परिणाम प्राप्त कर सकते हैं।

Ignore(parentObject => parentObject.ChildObjectOrCollection);

यह मूल रूप से ईएफ को मॉडल से "चाइल्डऑबजेक्टऑक्रोलिनेशन" संपत्ति को बाहर करने के लिए बताएगा ताकि इसे डेटाबेस में मैप न किया जाए।


"इग्नोर" का किस संदर्भ में उपयोग किया जा रहा है? यह प्रकल्पित संदर्भ में मौजूद नहीं है।
T3.0

0

मुझे एंटिटी फ्रेमवर्क कोर 3.1.0 का उपयोग करने के लिए एक समान चुनौती थी, मेरा रेपो लॉजिक काफी सामान्य है।

यह मेरे लिए काम किया:

builder.Entity<ChildEntity>().HasOne(c => c.ParentEntity).WithMany(l =>
       l.ChildEntity).HasForeignKey("ParentEntityId");

कृपया ध्यान दें कि "ParentEntityId" चाइल्ड यूनिट पर विदेशी कुंजी कॉलम नाम है। मैंने इस पद्धति पर उपर्युक्त पंक्ति को जोड़ा:

protected override void OnModelCreating(ModelBuilder builder)...
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.