EF4 POCO ऑब्जेक्ट्स के परिवर्तनों को सहेजते समय संबंधों को अपडेट करें


107

इकाई फ्रेमवर्क 4, POCO ऑब्जेक्ट्स और ASP.Net MVC2। मेरे पास कई रिश्ते हैं, ब्लॉगपोस्ट और टैग संस्थाओं के बीच एक-दूसरे के बारे में बताते हैं। इसका मतलब यह है कि मेरे T4 ने POCO BlogPost क्लास को जनरेट किया है:

public virtual ICollection<Tag> Tags {
    // getter and setter with the magic FixupCollection
}
private ICollection<Tag> _tags;

मैं ObjectContext के उदाहरण से एक BlogPost और संबंधित टैग के लिए पूछता हूं और इसे दूसरी परत पर भेजता हूं (MVC एप्लिकेशन में देखें)। बाद में मुझे बदले हुए गुणों और बदले हुए रिश्तों के साथ अपडेटेड ब्लॉगपोस्ट वापस मिल गया। उदाहरण के लिए इसमें "ए" "बी" और "सी" टैग थे, और नए टैग "सी" और "डी" हैं। मेरे विशेष उदाहरण में कोई नया टैग नहीं है और टैग के गुण कभी नहीं बदलते हैं, इसलिए केवल एक चीज जिसे सहेजा जाना चाहिए वह है परिवर्तित रिश्ते। अब मुझे इसे अन्य ObjectContext में सहेजने की आवश्यकता है। (अपडेट: अब मैंने उसी संदर्भ में करने की कोशिश की और असफल भी रहा।)

समस्या: मैं इसे ठीक से रिश्तों को बचाने के लिए नहीं बना सकता। मैंने जो कुछ भी पाया मैंने कोशिश की:

  • नियंत्रक। UpdateModel और कंट्रोलर .ryUpdateModel काम नहीं करते।
  • पुराने BlogPost को संदर्भ से प्राप्त करना तब संग्रह को संशोधित करना काम नहीं करता है। (अगले बिंदु से अलग तरीकों के साथ)
  • यह शायद काम करेगा, लेकिन मुझे उम्मीद है कि यह सिर्फ एक समाधान है, समाधान नहीं :(।
  • प्रत्येक संभावित संयोजनों में BlogPost और / या टैग के लिए संलग्न / जोड़ें / ChangeObjectState कार्य करने का प्रयास करें। अनुत्तीर्ण होना।
  • ऐसा लगता है कि मुझे क्या चाहिए, लेकिन यह काम नहीं करता है (मैंने इसे ठीक करने की कोशिश की, लेकिन अपनी समस्या के लिए नहीं कर सकता)।
  • ChangeState / Add / Attach / ... संबंध वस्तुओं को संदर्भ में लाने की कोशिश की। अनुत्तीर्ण होना।

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

एक समस्या जिसके बारे में मुझे पता है और एक स्वचालित सरल समाधान खोजना असंभव बना सकता है: जब एक POCO ऑब्जेक्ट का संग्रह बदल जाता है, जो कि उपर्युक्त आभासी संग्रह संपत्ति द्वारा होना चाहिए, क्योंकि तब FixupCollection ट्रिक दूसरे छोर पर रिवर्स संदर्भों को अपडेट कर देगी। कई-कई संबंधों के। हालाँकि जब कोई अद्यतन BlogPost ऑब्जेक्ट "व्यू" लौटाता है, तो ऐसा नहीं हुआ। इसका मतलब है कि शायद मेरी समस्या का कोई सरल समाधान नहीं है, लेकिन इससे मुझे बहुत दुःख होगा और मुझे EF4-POCO-MVC की जीत से नफरत होगी :(। इसके अलावा इसका मतलब यह होगा कि EF MVC वातावरण में ऐसा नहीं कर सकता है EF4 ऑब्जेक्ट प्रकार का उपयोग किया जाता है :(। मुझे लगता है कि स्नैपशॉट आधारित परिवर्तन ट्रैकिंग को यह पता लगाना चाहिए कि परिवर्तित ब्लॉगपोस्ट में मौजूदा पीके के साथ टैग के संबंध हैं।

Btw: मुझे लगता है कि एक ही समस्या एक-से-कई संबंधों के साथ होती है (Google और मेरे सहयोगी ऐसा कहते हैं)। मैं इसे घर पर कोशिश करूँगा, लेकिन फिर भी अगर वह काम करता है जो मेरे छह कई-से-कई रिश्तों में मेरी मदद नहीं करता है :(


कृपया अपना कोड पोस्ट करें। यह एक सामान्य परिदृश्य है।
जॉन फैरेल

1
मेरे पास इस समस्या का एक स्वचालित हल है, यह नीचे दिए गए उत्तरों में छिपा है, बहुत से इसे याद करेंगे, लेकिन कृपया एक नज़र डालें क्योंकि यह आपको यहां
लेगा

@brentmckendrick मुझे लगता है कि एक और दृष्टिकोण बेहतर है। तार पर पूरे संशोधित वस्तु ग्राफ भेजने के बजाय, सिर्फ डेल्टा क्यों नहीं भेजें? तुम भी उस मामले में डीटीओ वर्गों उत्पन्न की आवश्यकता नहीं होगी। यदि आपके पास इस बारे में कोई राय है, तो कृपया चर्चा करें stackoverflow.com/questions/1344066/calculate-object-delta पर
HappyNomad

जवाबों:


145

आइए इसे इस तरह आज़माएँ:

  • संदर्भ के लिए BlogPost संलग्न करें। ऑब्जेक्ट की स्थिति के संदर्भ में ऑब्जेक्ट को संलग्न करने के बाद, सभी संबंधित ऑब्जेक्ट और सभी संबंध अपरिवर्तित पर सेट होते हैं।
  • अपने BlogPost को संशोधित करने के लिए संदर्भ .OjectStateManager.ChangeObjectState का उपयोग करें
  • टैग संग्रह के माध्यम से Iterate
  • वर्तमान टैग और BlogPost के बीच संबंध स्थापित करने के लिए स्थिति सेट करने के लिए Reference.ObjectStateManager.ChangeRelationshipState का उपयोग करें।
  • परिवर्तनों को सुरक्षित करें

संपादित करें:

मुझे लगता है कि मेरी एक टिप्पणी ने आपको झूठी आशा दी है कि ईएफ आपके लिए मर्ज करेगा। मैंने इस समस्या के साथ बहुत कुछ खेला और मेरा निष्कर्ष कहता है कि ईएफ आपके लिए ऐसा नहीं करेगा। मुझे लगता है कि आपको MSDN पर मेरा प्रश्न भी मिल गया है । वास्तव में इंटरनेट पर इस तरह के बहुत सारे सवाल हैं। समस्या यह है कि यह स्पष्ट रूप से नहीं बताया गया है कि इस परिदृश्य से कैसे निपटा जाए। तो समस्या पर एक नजर डालते हैं:

समस्या पृष्ठभूमि

ईएफ को संस्थाओं पर परिवर्तनों को ट्रैक करने की आवश्यकता है ताकि दृढ़ता को पता हो कि कौन से रिकॉर्ड को अपडेट करना, सम्मिलित करना या हटाना है। समस्या यह है कि यह परिवर्तनों को ट्रैक करने के लिए ऑब्जेक्टकंटेक्स्ट जिम्मेदारी है। ObjectContext केवल संलग्न संस्थाओं के लिए परिवर्तनों को ट्रैक करने में सक्षम है। ObjectContext के बाहर बनाई जाने वाली एंटिटीज़ को ट्रैक नहीं किया जाता है।

समस्या का विवरण

उपरोक्त विवरण के आधार पर हम स्पष्ट रूप से कह सकते हैं कि ईएफ कनेक्टेड परिदृश्यों के लिए अधिक उपयुक्त है जहां इकाई हमेशा संदर्भ से जुड़ी होती है - WinForm आवेदन के लिए विशिष्ट। वेब अनुप्रयोगों के लिए डिस्कनेक्ट किए गए परिदृश्य की आवश्यकता होती है जहां अनुरोध प्रसंस्करण के बाद संदर्भ बंद हो जाता है और क्लाइंट के लिए HTTP प्रतिक्रिया के रूप में इकाई सामग्री पारित हो जाती है। अगला HTTP अनुरोध, उस इकाई की संशोधित सामग्री प्रदान करता है जिसे नए संदर्भ से जुड़ा और फिर से बनाया जाना है। मनोरंजन आमतौर पर संदर्भ क्षेत्र के बाहर खुश रहता है (निरंतर अज्ञानता के साथ स्तरित वास्तुकला)।

उपाय

तो ऐसे डिस्कनेक्टेड परिदृश्य से कैसे निपटें? POCO वर्गों का उपयोग करते समय हमारे पास परिवर्तन ट्रैकिंग से निपटने के 3 तरीके हैं:

  • स्नैपशॉट - डिस्कनेक्ट किए गए परिदृश्य के लिए समान संदर्भ = बेकार की आवश्यकता होती है
  • डायनेमिक ट्रैकिंग प्रॉक्सी - के लिए समान संदर्भ = डिस्कनेक्ट किए गए परिदृश्य के लिए बेकार की आवश्यकता होती है
  • मैनुअल सिंक्रनाइज़ेशन।

एकल इकाई पर मैनुअल सिंक्रनाइज़ेशन आसान काम है। आपको बस इकाई संलग्न करने और डालने के लिए AddObject को कॉल करने की आवश्यकता है, हटाने के लिए DeleteObject या अद्यतन करने के लिए संशोधित करने के लिए ObjectStateManager में स्थिति सेट करें। असली दर्द तब होता है जब आपको एकल इकाई के बजाय ऑब्जेक्ट ग्राफ से निपटना पड़ता है। यह दर्द तब और भी बदतर होता है जब आपको स्वतंत्र संघों (जो विदेशी कुंजी संपत्ति का उपयोग नहीं करते हैं) और कई संबंधों से संबंधित होता है। उस स्थिति में आपको ऑब्जेक्ट ग्राफ में प्रत्येक इकाई को मैन्युअल रूप से सिंक्रनाइज़ करना होगा लेकिन ऑब्जेक्ट ग्राफ में प्रत्येक संबंध भी।

MSDN प्रलेखन द्वारा समाधान के रूप में मैनुअल सिंक्रोनाइज़ेशन प्रस्तावित है: संलग्न और कोचिंग ऑब्जेक्ट कहता है:

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

उल्लेख किए गए तरीके ChangeObjectState और ChangeRelationshipState of ObjectStateManager = मैन्युअल परिवर्तन ट्रैकिंग हैं। इसी तरह का प्रस्ताव अन्य MSDN प्रलेखन लेख में है: संबंधों को परिभाषित और प्रबंधित करना कहते हैं:

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

इसके अलावा EF v1 से संबंधित ब्लॉग पोस्ट है जो EF के इस व्यवहार की बिल्कुल आलोचना करता है।

समाधान का कारण

EF में कई "सहायक" संचालन और सेटिंग्स हैं जैसे कि ताज़ा , लोड , ApplyCurrentValues , ApplyOriginalValues , MergeOption आदि। लेकिन मेरी जांच से ये सभी सुविधाएँ केवल एकल इकाई के लिए काम करती हैं और केवल स्केलर प्रीपरेटी (= नेविगेशन गुण और संबंध नहीं) को प्रभावित करती हैं। मैं इस पद्धति का परीक्षण इकाई में निहित जटिल प्रकारों के साथ नहीं करता।

अन्य प्रस्तावित समाधान

वास्तविक मर्ज कार्यक्षमता के बजाय EF टीम सेल्फ ट्रैकिंग एंटिटीज (STE) नामक कुछ प्रदान करती है जो समस्या का समाधान नहीं करती है। सबसे पहले STE तभी काम करता है जब पूरे प्रसंस्करण के लिए एक ही उदाहरण का उपयोग किया जाता है। वेब एप्लिकेशन में यह तब तक नहीं है जब तक आप राज्य या सत्र को देखने के लिए स्टोर नहीं करते। इसके कारण मैं EF का उपयोग करने से बहुत दुखी हूं और मैं NHibernate की सुविधाओं की जांच करने जा रहा हूं। पहला अवलोकन कहता है कि NHibernate में शायद ऐसी कार्यक्षमता है

निष्कर्ष

मैं MSDN फोरम पर किसी अन्य संबंधित प्रश्न के एकल लिंक के साथ इस धारणा को समाप्त करूंगा । ज़ीशान हिरानी के जवाब की जाँच करें। वह Entity Framework 4.0 व्यंजनों के लेखक हैं । यदि वह कहता है कि ऑब्जेक्ट ग्राफ़ का स्वचालित मर्ज समर्थित नहीं है, तो मुझे विश्वास है।

लेकिन फिर भी संभावना है कि मैं पूरी तरह से गलत हूं और ईएफ में कुछ स्वचालित मर्ज कार्यक्षमता मौजूद है।

2 संपादित करें:

जैसा कि आप देख सकते हैं कि यह 2007 में सुझाव के रूप में पहले से ही MS Connect में जोड़ा गया था । MS ने इसे अगले संस्करण में किए जाने वाले कुछ के रूप में बंद कर दिया है, लेकिन वास्तव में STE को छोड़कर इस अंतर को सुधारने के लिए कुछ भी नहीं किया गया था।


7
यह एक सबसे अच्छा उत्तर है जो मैंने SO पर पढ़ा है। आपने स्पष्ट रूप से कहा है कि विषय पर इतने सारे MSDN लेख, प्रलेखन और ब्लॉग पोस्ट विफल हुए हैं। EF4 स्वाभाविक रूप से "अलग" संस्थाओं से अद्यतन संबंधों का समर्थन नहीं करता है। यह केवल आपको इसे स्वयं कार्यान्वित करने के लिए उपकरण प्रदान करता है। धन्यवाद!
tyriker

1
तो पिछले कुछ महीनों के बाद, EF4 की तुलना में NHibernate इस मुद्दे से कैसे संबंधित है?
CallMeLaNN

1
यह NHibernate में बहुत अच्छी तरह से समर्थित है :-) मैन्युअल रूप से विलय करने की कोई आवश्यकता नहीं है, मेरे उदाहरण में यह 3-स्तरीय गहरी वस्तु ग्राफ है, प्रश्न के उत्तर हैं, प्रत्येक उत्तर में टिप्पणियां हैं, और प्रश्न में टिप्पणियां भी हैं। NHibernate अपनी ऑब्जेक्ट ग्राफ को बना / मर्ज कर सकता है, चाहे वह कितना भी जटिल क्यों न हो ienablemuch.com/2011/01/nhibernate-saves-your-whole-object.html एक और संतुष्ट NHibernate उपयोगकर्ता: codinstinstinct.com/2009/11/…
माइकल Buen

2
सबसे अच्छा स्पष्टीकरण जो मैंने कभी पढ़ा है !! बहुत बहुत धन्यवाद
marvelTracker

2
EF टीम की योजना इस पोस्ट EF6 को संबोधित करने की है। आप चाहें तो Unitframework.codeplex.com/workitem/864 के
Eric J.

19

मेरे पास उस समस्या का हल है जो लदिस्लाव द्वारा ऊपर वर्णित की गई थी। मैंने DbContext के लिए एक एक्सटेंशन विधि बनाई है जो स्वचालित रूप से प्रदान किए गए ग्राफ़ और जारी ग्राफ के एक अंतर के आधार पर ऐड / अपडेट / डिलीट का प्रदर्शन करेगी।

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

कृपया देखें और देखें कि क्या यह http://refactorthis.wordpress.com/2012/12/11/introducing-graphdiff-for-entity-framework-code-first-allowing-automated-updates-of-a- मदद कर सकता है ग्राफ के- अलग-संस्थाओं /

आप सीधे यहां https://github.com/refactorthis/GraphDiff कोड पर जा सकते हैं


मुझे यकीन है कि आप इस प्रश्न को आसानी से हल कर सकते हैं, मैं इसके साथ बुरा समय बिता रहा हूं।
शिमी वेइटहैंडलर

1
हाय शिम्मी, सॉरी आखिरकार कुछ समय के लिए लग गया। मैं आज रात इसे देखूंगा।
ब्रेंटमेकेंड्रिक

यह पुस्तकालय महान है और मुझे बहुत समय बचा लिया है! धन्यवाद!
लॉर्डजैब

9

मुझे पता है कि यह ओपी के लिए देर हो चुकी है, लेकिन चूंकि यह एक बहुत ही सामान्य मुद्दा है, इसलिए मैंने इसे किसी और की सेवा के मामले में पोस्ट किया है। मैं इस मुद्दे के साथ आसपास रहा हूँ और मुझे लगता है कि मुझे एक सरल समाधान मिला है, मैं क्या करूँ:

  1. अपने राज्य को संशोधित करके मुख्य ऑब्जेक्ट (उदाहरण के लिए ब्लॉग) सहेजें।
  2. अद्यतन किए गए संग्रह सहित अद्यतन ऑब्जेक्ट के लिए डेटाबेस को क्वेरी करें।
  3. क्वेरी और कन्वर्ट .TLList () वे इकाइयाँ जिन्हें मैं अपने संग्रह में शामिल करना चाहता हूँ।
  4. चरण 3 से प्राप्त सूची में मुख्य ऑब्जेक्ट के संग्रह को अपडेट करें।
  5. परिवर्तनों को सुरक्षित करें();

निम्नलिखित उदाहरण में "dataobj" और "_Categories" मेरे नियंत्रक द्वारा प्राप्त पैरामीटर हैं "dataobj" मेरी मुख्य वस्तु है, और "_categories" एक IEnumerable है जिसमें उन श्रेणियों की आईडी होती है जिन्हें उपयोगकर्ता ने दृश्य में चुना है।

    db.Entry(dataobj).State = EntityState.Modified;
    db.SaveChanges();
    dataobj = db.ServiceTypes.Include(x => x.Categories).Single(x => x.Id == dataobj.Id);
    var it = _categories != null ? db.Categories.Where(x => _categories.Contains(x.Id)).ToList() : null;
    dataobj.Categories = it;
    db.SaveChanges();

यह कई संबंधों के लिए भी काम करता है


7

एंटिटी फ्रेमवर्क टीम को पता है कि यह एक प्रयोज्य मुद्दा है और इसे EF6 के बाद संबोधित करने की योजना है।

इकाई फ्रेमवर्क टीम से:

यह एक प्रयोज्य मुद्दा है जिसके बारे में हम जानते हैं और कुछ ऐसा है जिसके बारे में हम सोचते रहे हैं और EF6 के बाद अधिक काम करने की योजना बना रहे हैं। मैंने समस्या को ट्रैक करने के लिए यह कार्य आइटम बनाया है: http://entityframework.codeplex.com/workitem/864 इस कार्य आइटम में इसके लिए उपयोगकर्ता की आवाज आइटम का लिंक भी है - मैं आपको इसके लिए वोट करने के लिए प्रोत्साहित करता हूं यदि आपके पास है पहले से ऐसा नहीं किया गया।

यदि यह आपको प्रभावित करता है, तो इस सुविधा के लिए मतदान करें

http://entityframework.codeplex.com/workitem/864


पोस्ट-EF6? आशावादी मामले में यह किस वर्ष होगा?
quetzalcoatl 21

@quetzalcoatl: कम से कम यह उनके रडार पर है :-) EF 1 E के बाद से एक ढीला तरीका आया है, लेकिन अभी भी एक रास्ता तय करना है।
एरिक जे।

1

समस्या के बारे में बताने के लिए सभी जवाब बहुत अच्छे थे, लेकिन उनमें से किसी ने भी वास्तव में मेरे लिए समस्या का समाधान नहीं किया।

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

VB के लिए क्षमा करें, लेकिन मैं जिस प्रोजेक्ट में काम कर रहा हूं, वह इसमें लिखा गया है।

मूल इकाई "रिपोर्ट" का "रिपोर्टरॉल" से कई संबंध है और संपत्ति "रिपोर्टरॉल्स" है। नई भूमिकाओं को एक अजाक्स कॉल से एक अल्पविराम द्वारा अलग स्ट्रिंग द्वारा पारित किया जाता है।

पहली पंक्ति सभी बाल संस्थाओं को हटा देगी, और अगर मैंने "db.ReportRoles.Remove (f)" के बजाय "Report.ReportRoles.Remove (f)" का उपयोग किया था तो मुझे त्रुटि मिलेगी।

report.ReportRoles.ToList.ForEach(Function(f) db.ReportRoles.Remove(f))
Dim newRoles = If(String.IsNullOrEmpty(model.RolesString), New String() {}, model.RolesString.Split(","))
newRoles.ToList.ForEach(Function(f) db.ReportRoles.Add(New ReportRole With {.ReportId = report.Id, .AspNetRoleId = f}))
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.