क्या मैं एक अलग लेकिन समान ऑब्जेक्ट का उपयोग करके संलग्न वस्तु को अपडेट कर सकता हूं?


10

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

My ORM लेयर Entity-Framework है। मूवी वर्ग इस तरह दिखता है:

class Movie
{
    public virtual ICollection<Language> SpokenLanguages { get; set; }
    public virtual ICollection<Genre> Genres { get; set; }
    public virtual ICollection<Keyword> Keywords { get; set; }
}

समस्या तब होती है जब मेरे पास एक फिल्म होती है जिसे अपडेट करने की आवश्यकता होती है: मेरा डेटाबेस ट्रैक की जा रही वस्तु के बारे में सोचेगा और नए एपीआई कॉल से प्राप्त नई वस्तु को अलग-अलग ऑब्जेक्ट के रूप में अवहेलना करता है .Equals()

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

भाषाओं के साथ मेरे पास यह मुद्दा था और मेरा समाधान संलग्न भाषा की वस्तुओं की खोज करना था, उन्हें संदर्भ से अलग करना, उनके पीके को अद्यतन वस्तु में ले जाना और उस संदर्भ में संलग्न करना था। जब SaveChanges()अब निष्पादित किया जाता है, तो यह अनिवार्य रूप से इसे बदल देगा।

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

क्या इसे और अधिक सुरुचिपूर्ण ढंग से करने का कोई तरीका है? आदर्श रूप से मैं केवल अद्यतन फिल्म को संदर्भ में पास करना चाहता हूं और यह है कि Equals()विधि के आधार पर अपडेट करने के लिए सही मूवी का चयन करें , अपने सभी क्षेत्रों को अपडेट करें और प्रत्येक जटिल ऑब्जेक्ट के लिए: अपने स्वयं के Equals()तरीके के आधार पर फिर से मौजूदा रिकॉर्ड का उपयोग करें और सम्मिलित करें यह अभी तक मौजूद नहीं है।

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


आप अपने एपीआई डेटा से सिर्फ ट्रैक की गई इकाई को अपडेट क्यों नहीं कर सकते हैं और संस्थाओं को बदलने / अलग करने / मिलान / संलग्न करने के बिना परिवर्तनों को बचा सकते हैं?
Si-N

@ Si-N: क्या आप इसका विस्तार कर सकते हैं कि वास्तव में यह कैसे होगा?
जीरो वेनवेल

ठीक है अब आपने जोड़ा है कि अंतिम पैराग्राफ यह अधिक समझ में आता है, आप उन्हें अपडेट करने से पहले संस्थाओं को पुनः प्राप्त करने से बचने की कोशिश कर रहे हैं? ऐसा कुछ नहीं है जो आपकी मूवी क्लास में आपका पीके हो सकता है? आप बाहरी एपीआई से लेकर अपनी संस्थाओं तक की फिल्मों का मिलान कैसे कर रहे हैं? आप वैसे भी एक db कॉल में अपडेट करने के लिए आवश्यक सभी संस्थाओं को पुनः प्राप्त कर सकते हैं, क्या यह सरल समाधान नहीं होगा जो एक बड़े प्रदर्शन को हिट न करे (जब तक कि आप बड़ी संख्या में फिल्मों को अपडेट करने की बात नहीं कर रहे हैं)?
सी-एन

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

जवाबों:


8

मुझे वह नहीं मिला, जिसकी मैं उम्मीद कर रहा था, लेकिन मुझे मौजूदा चयन-अलग-अपडेट-अटैच अनुक्रम पर एक सुधार मिला।

विस्तार विधि AddOrUpdate(this DbSet)आपको वही करने की अनुमति देती है जो मैं करना चाहता हूं: यदि यह नहीं है तो सम्मिलित करें और यदि यह मौजूदा मूल्य पाया जाता है तो अपडेट करें। जब से मैंने वास्तव में इसे केवल seed()माइग्रेशन के साथ संयोजन में विधि में उपयोग करने के लिए देखा है, तब से मुझे इस का उपयोग करने का एहसास नहीं हुआ । अगर कोई कारण है कि मुझे इसका उपयोग नहीं करना चाहिए, तो मुझे बताएं।

नोट करने के लिए कुछ उपयोगी: एक अधिभार उपलब्ध है जो आपको विशेष रूप से चयन करने की अनुमति देता है कि समानता कैसे निर्धारित की जानी चाहिए। यहाँ मैं अपने इस्तेमाल कर सकता था, TMDbIdलेकिन मैंने इसके बजाय पूरी तरह से अपनी खुद की आईडी की अवहेलना करने का विकल्प चुना और इसके बजाय TMDbId पर एक PK का उपयोग किया DatabaseGeneratedOption.None। मैं इस उपागम पर प्रत्येक दृष्टिकोण का उपयोग करता हूं, जहां उपयुक्त हो।

स्रोत का दिलचस्प हिस्सा :

internalSet.InternalContext.Owner.Entry(existing).CurrentValues.SetValues(entity);

जो वास्तव में हुड के तहत डेटा को अपडेट किया जाता है।

जो कुछ बचा है वह AddOrUpdateप्रत्येक वस्तु पर कॉल कर रहा है जिसे मैं इससे प्रभावित होना चाहता हूं:

public void InsertOrUpdate(Movie movie)
{
    _context.Movies.AddOrUpdate(movie);
    _context.Languages.AddOrUpdate(movie.SpokenLanguages.ToArray());
    // Other objects/collections
    _context.SaveChanges();
}

यह उतना साफ नहीं है जितना मुझे आशा है कि मुझे अपनी वस्तु के प्रत्येक टुकड़े को मैन्युअल रूप से निर्दिष्ट करना होगा जिसे अद्यतन करने की आवश्यकता है लेकिन यह उतना ही करीब होगा जितना इसे मिलेगा।

संबंधित पढ़ना: /programming/15336248/entity-framework-5-updating-a-record


अपडेट करें:

यह पता चला है कि मेरे परीक्षण पर्याप्त कठोर नहीं थे। इस तकनीक का उपयोग करने के बाद मैंने देखा कि जब नई भाषा जोड़ी गई थी, तो यह फिल्म से जुड़ी नहीं थी। कई-कई-कई तालिका में। यह एक ज्ञात लेकिन कम-प्राथमिकता वाला मुद्दा है और जहां तक ​​मुझे पता है यह तय नहीं किया गया है।

अंत में मैंने उस दृष्टिकोण के लिए जाने का फैसला किया जहां मेरे पास Update(T)प्रत्येक प्रकार के तरीके हैं और घटनाओं के इस क्रम का पालन करें:

  • नई वस्तु में संग्रह पर लूप
  • प्रत्येक संग्रह में प्रत्येक प्रविष्टि के लिए, यह डेटाबेस में ऊपर देखो
  • यदि यह मौजूद है, तो इसे Update()नए मानों के साथ अद्यतन करने के लिए विधि का उपयोग करें
  • यदि यह मौजूद नहीं है, यह उचित DbSet में जोड़ें
  • संलग्न वस्तुओं लौटें और संलग्न वस्तुओं के संग्रह के साथ जड़ वस्तु में संग्रह की जगह
  • रूट ऑब्जेक्ट ढूंढें और अपडेट करें

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


इसे और साफ करने के बाद मैं अब इस विधि का उपयोग करता हूं:

private IEnumerable<T> InsertOrUpdate<T, TKey>(IEnumerable<T> entities, Func<T, TKey> idExpression) where T : class
{
    foreach (var entity in entities)
    {
        var existingEntity = _context.Set<T>().Find(idExpression(entity));
        if (existingEntity != null)
        {
            _context.Entry(existingEntity).CurrentValues.SetValues(entity);
            yield return existingEntity;
        }
        else
        {
            _context.Set<T>().Add(entity);
            yield return entity;
        }
    }
    _context.SaveChanges();
}

यह मैं इसे इस तरह कहते हैं और डालने / अंतर्निहित संग्रह अद्यतन करने के लिए अनुमति देता है:

movie.Genres = new List<Genre>(InsertOrUpdate(movie.Genres, x => x.TmdbId));

ध्यान दें कि मैंने मूल रूट ऑब्जेक्ट को पुनः प्राप्त मान को कैसे पुन: असाइन किया है: अब यह प्रत्येक संलग्न ऑब्जेक्ट से जुड़ा हुआ है। जड़ वस्तु (फिल्म) अपडेट कर रहा है उसी तरह से किया जाता है:

var localMovie = _context.Movies.SingleOrDefault(x => x.TmdbId == movie.TmdbId);
if (localMovie == null)
{
    _context.Movies.Add(movie);
} 
else
{
    _context.Entry(localMovie).CurrentValues.SetValues(movie);
}

1-एम संबंध में आप कैसे डिलीट कर रहे हैं? उदाहरण के लिए, 1-मूवी में कई भाषाएं हो सकती हैं; यदि किसी एक भाषा को हटा दिया जाता है, तो क्या आपका कोड इसे हटा रहा है? यह आपके समाधान को केवल आवेषण और अपडेट (लेकिन हटा नहीं है?) दिखाई देता है
joedotnot

0

जब से आप विभिन्न क्षेत्रों के साथ काम कर रहे हैं idऔर tmbid, मेरा सुझाव है कि एपीआई को अपडेट करके सभी जानकारी जैसे कि शैलियों, भाषाओं, कीवर्ड्स आदि का एक और अलग इंडेक्स बनाएं ... और फिर इकट्ठा करने के बजाय इंडेक्स और जानकारी के लिए कॉल करें। आपके मूवी वर्ग में विशिष्ट वस्तु के बारे में पूरी जानकारी।


1
मैं यहाँ विचार की ट्रेन का अनुसरण नहीं करता। क्या आप विस्तार कर सकते हैं? ध्यान दें कि बाहरी एपीआई पूरी तरह से मेरे नियंत्रण से बाहर है।
जीरोने वेनवेल
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.