एंटिटी फ्रेमवर्क में सबसे तेज़ तरीका


682

मैं एंटिटी फ्रेमवर्क में डालने का सबसे तेज़ तरीका ढूंढ रहा हूं।

मैं यह इस परिदृश्य के कारण पूछ रहा हूं जहां आपके पास एक सक्रिय लेनदेन है और प्रविष्टि विशाल (4000+) है। यह संभावित रूप से 10 मिनट (लेन-देन का डिफ़ॉल्ट समय) से अधिक हो सकता है, और इससे अपूर्ण लेनदेन होगा।


1
वर्तमान में आप इसे कैसे कर रहे हैं?
डस्टिन लैइन

TransactionScope बनाना, DBContext को इंस्टेंट करना, कनेक्शन को खोलना, और प्रत्येक कथन में सम्मिलन और SaveChanges (प्रत्येक रिकॉर्ड के लिए) करते हुए, नोट: TransactionScope और DBContext कथन का उपयोग करने में हैं, और मैं कनेक्शन को अंत में बंद कर रहा हूं। ब्लॉक
बोंगो शार्प

संदर्भ के लिए एक और जवाब: stackoverflow.com/questions/5798646/…
लादिस्लाव मृका

2
SQL डेटाबेस में डालने का सबसे तेज़ तरीका EF शामिल नहीं है। AFAIK इसके BCP फिर TVP + मर्ज / डालें।
स्टिंगजैक

1
उन लोगों के लिए जो टिप्पणियों को पढ़ेंगे: सबसे अधिक लागू, आधुनिक उत्तर यहां है।
तनवीर बदर

जवाबों:


986

अपने प्रश्न के लिए टिप्पणी में अपनी टिप्पणी के लिए:

"... SaveChanges ( प्रत्येक रिकॉर्ड के लिए ) ..."

यह सबसे खराब चीज है जो आप कर सकते हैं! SaveChanges()प्रत्येक रिकॉर्ड के लिए कॉलिंग थोक आवेषण को बहुत धीमा कर देती है। मैं कुछ सरल परीक्षण करूंगा जिससे प्रदर्शन में सुधार होगा:

  • SaveChanges()सभी रिकॉर्ड के बाद एक बार कॉल करें ।
  • SaveChanges()उदाहरण के लिए 100 रिकॉर्ड के बाद कॉल करें ।
  • SaveChanges()उदाहरण के लिए 100 रिकॉर्ड के बाद कॉल करें और संदर्भ को हटा दें और एक नया बनाएं।
  • परिवर्तन का पता लगाना अक्षम करें

थोक आवेषण के लिए मैं इस तरह एक पैटर्न के साथ काम कर रहा हूं और प्रयोग कर रहा हूं:

using (TransactionScope scope = new TransactionScope())
{
    MyDbContext context = null;
    try
    {
        context = new MyDbContext();
        context.Configuration.AutoDetectChangesEnabled = false;

        int count = 0;            
        foreach (var entityToInsert in someCollectionOfEntitiesToInsert)
        {
            ++count;
            context = AddToContext(context, entityToInsert, count, 100, true);
        }

        context.SaveChanges();
    }
    finally
    {
        if (context != null)
            context.Dispose();
    }

    scope.Complete();
}

private MyDbContext AddToContext(MyDbContext context,
    Entity entity, int count, int commitCount, bool recreateContext)
{
    context.Set<Entity>().Add(entity);

    if (count % commitCount == 0)
    {
        context.SaveChanges();
        if (recreateContext)
        {
            context.Dispose();
            context = new MyDbContext();
            context.Configuration.AutoDetectChangesEnabled = false;
        }
    }

    return context;
}

मेरे पास एक परीक्षण कार्यक्रम है जो DB में 560.000 संस्थाओं (9 स्केलर गुण, कोई नेविगेशन गुण) को सम्मिलित नहीं करता है। इस कोड के साथ यह 3 मिनट से भी कम समय में काम करता है।

प्रदर्शन के लिए SaveChanges()"कई" रिकॉर्ड ("कई" लगभग 100 या 1000) के बाद कॉल करना महत्वपूर्ण है । यह SaveChanges के बाद संदर्भ को हटाने और एक नया बनाने के लिए प्रदर्शन में सुधार करता है। यह सभी संदर्भों से संदर्भ को साफ करता है, SaveChangesऐसा नहीं करता है, राज्य में अभी भी संस्थाएं संदर्भ से जुड़ी हुई हैं Unchanged। यह संदर्भ में संलग्न संस्थाओं का बढ़ता आकार है जो कदम से सम्मिलन कदम को धीमा कर देता है। तो, यह कुछ समय बाद इसे साफ करने में मददगार है।

यहाँ मेरी 560000 संस्थाओं के लिए कुछ माप दिए गए हैं:

  • प्रतिबद्ध = 1, फिर से बनाएँ = गलत: कई घंटे (यह आपकी वर्तमान प्रक्रिया है)
  • कमिटमेंट = १००, फिर से तैयार करें = गलत: २० मिनट से अधिक
  • कमिटमेंट = १०००, रीक्रिएट कॉन्टेक्स्ट = गलत: २४२ सेकंड
  • कमिटमेंट = १००००, रीक्रिएट कॉन्टेक्स्ट = गलत: २०२ सेकंड
  • कमिटमेंट = १०००००, रीक्रिएट कॉन्टेक्स्ट = गलत: १ ९९ सेकंड
  • कमेटाउंट = 1000000, रिक्रिएंट कॉन्टेक्स्ट = गलत: मेमोरी अपवाद से बाहर
  • कमिटमेंट = 1, फिर से बनाएँ = सही: 10 मिनट से अधिक
  • कमिटमेंट = 10, रीक्रिएट कॉन्टेक्स्ट = सच: 241 सेकंड
  • प्रतिबद्ध = 100, फिर से बनाएँ = सही: 164 सेकंड
  • कमिटमेंट = १०००, रीक्रिएट कॉन्टेक्स्ट = सच: १ ९ १ सेकंड

ऊपर पहले परीक्षण में व्यवहार यह है कि प्रदर्शन बहुत गैर-रैखिक है और समय के साथ बेहद कम हो जाता है। ("कई घंटे" एक अनुमान है, मैंने इस परीक्षण को कभी समाप्त नहीं किया, मैंने 20 मिनट के बाद 50.000 संस्थाओं पर रोक दिया।) यह गैर-रैखिक व्यवहार अन्य सभी परीक्षणों में इतना महत्वपूर्ण नहीं है।


89
@ बोंगो शार्प: DbContext AutoDetectChangesEnabled = false;पर सेट करना न भूलें । इसका एक बड़ा अतिरिक्त प्रदर्शन प्रभाव भी है: stackoverflow.com/questions/5943394/…
सालामा

6
हाँ, समस्या यह है कि मैं Entity फ्रेमवर्क 4 का उपयोग कर रहा हूं, और AutoDetectChangesEnabled 4.1 का हिस्सा है, फिर भी, मैंने प्रदर्शन परीक्षण किया और मेरे पास अद्भुत परिणाम थे, यह 00:12:00 से 00:00:22 SavinChanges तक चला गया प्रत्येक इकाई पर olverload कर रहा था ... धन्यवाद अपने answare के लिए बहुत कुछ! यह वही है जिसकी मैं तलाश कर रहा था
बोंगो शार्प

10
संदर्भ के लिए धन्यवाद। Configuration.AutoDetectChangesEnabled = false; टिप, यह एक बड़ा फर्क पड़ता है।
douglaz

1
@ dahacker89: क्या आप सही संस्करण EF> = 4.1 का उपयोग कर रहे हैं और DbContextनहीं ObjectContext?
सुलामा

3
@ dahacker89: मेरा सुझाव है कि आप शायद अधिक विवरण के साथ अपनी समस्या के लिए एक अलग प्रश्न बनाएं। मैं यह पता लगाने में सक्षम नहीं हूं कि क्या गलत है।
सुलामा

176

यह संयोजन काफी अच्छी तरह से गति बढ़ाता है।

context.Configuration.AutoDetectChangesEnabled = false;
context.Configuration.ValidateOnSaveEnabled = false;

46
ValidateOnSaveEnabled को नेत्रहीन रूप से अक्षम न करें आप उस व्यवहार के आधार पर हो सकते हैं, और इसे तब तक महसूस नहीं कर सकते जब तक कि बहुत देर न हो जाए। फिर आप कोड में कहीं और सत्यापन कर रहे होंगे और EF को फिर से मान्य होने पर पूरी तरह से अनावश्यक है।
जेरेमी कुक

1
मेरे परीक्षण में 20.000 पंक्तियों की बचत 101 सेकंड से घटकर 88 सेकंड हो गई। बहुत कुछ नहीं है और इसके क्या निहितार्थ हैं।
ए.एच.

27
@JeremyCook मुझे लगता है कि आप जिस चीज़ को पाने की कोशिश कर रहे हैं, वह यह है कि यह उत्तर बहुत बेहतर होगा यदि यह इन गुणों को उनके डिफ़ॉल्ट मानों (प्रदर्शन सुधार से अलग) से बदलने के संभावित निहितार्थों को समझाया। मैं सहमत हूँ।
छद्मकोडर

1
यह मेरे लिए काम कर रहा है, हालांकि यदि आप संदर्भ में रिकॉर्ड अपडेट कर रहे हैं तो आपको DetectChanges () को स्पष्ट रूप से कॉल करना होगा
पहाड़ी सेप

2
इन्हें अक्षम किया जा सकता है और फिर कोशिश-अंत ब्लॉक के साथ फिर से सक्षम किया जा सकता है: msdn.microsoft.com/en-us/data/jj556205.aspx
yellavon

98

सबसे तेज़ तरीका बल्क इंसर्ट एक्सटेंशन का उपयोग करना होगा , जिसे मैंने विकसित किया

नोट: यह एक वाणिज्यिक उत्पाद है, नि: शुल्क नहीं

अधिकतम प्रदर्शन प्राप्त करने के लिए यह SqlBulkCopy और कस्टम डेटरीडर का उपयोग करता है। परिणामस्वरूप यह नियमित डालने या AddRange का उपयोग करने की तुलना में 20 गुना अधिक तेज है EntityFramework.BulkInsert बनाम EF AddRange

उपयोग अत्यंत सरल है

context.BulkInsert(hugeAmountOfEntities);

10
तेज़ लेकिन केवल एक पदानुक्रम की शीर्ष परत करता है।
सीएडी

65
यह स्वतंत्र नहीं है।
आमिर सनायन

72
विज्ञापन अधिक स्मार्ट हो रहे हैं ... यह एक फ्रीलांस के लिए उत्पाद और बहुत महंगा है। चेतावनी दी!
जुलियो क्यूसी

35
1 साल के समर्थन और उन्नयन के लिए USD600? तुम्हारा दिमाग खराब है?
कैमिलो तेरेविंटो

7
im नहीं उत्पाद के मालिक अब किसी भी
maxlego

83

आपको इसके System.Data.SqlClient.SqlBulkCopyलिए उपयोग करना चाहिए । यहाँ प्रलेखन है , और निश्चित रूप से ऑनलाइन ट्यूटोरियल बहुत सारे हैं।

क्षमा करें, मुझे पता है कि आप ईएफ प्राप्त करने के लिए एक सरल उत्तर की तलाश में थे कि आप क्या चाहते हैं, लेकिन थोक संचालन वास्तव में ओआरएम के लिए क्या नहीं है।


1
मैंने इस पर शोध करते हुए एक-दो बार SqlBulkCopy में भाग लिया है, लेकिन यह टेबल-टू-टेबल आवेषण के लिए अधिक उन्मुख प्रतीत होता है, दुख की बात है कि मैं आसान समाधान की उम्मीद नहीं कर रहा था, बल्कि प्रदर्शन युक्तियों, उदाहरण के लिए प्रबंधन की स्थिति कनेक्शन को मैन्युअल रूप से, ईएफ को आपके लिए करने के लिए
प्रेरित किया

7
मैंने अपने आवेदन से बड़ी मात्रा में डेटा सम्मिलित करने के लिए SqlBulkCopy का उपयोग किया है। आपको मूल रूप से एक DataTable बनाना है, इसे भरना है, फिर उस BulkCopy को पास करें । जब आप अपना डेटाटेबल सेट कर रहे होते हैं, तो कुछ गॅच हो जाते हैं (जिनमें से ज्यादातर मैं भूल गया, दुख की बात है), लेकिन यह ठीक काम करना चाहिए
एडम रैकिस

2
मैंने अवधारणा का प्रमाण दिया, और जैसा कि प्रॉमिस किया गया है, यह वास्तव में तेजी से काम करता है, लेकिन ईएफ का उपयोग करने के कारणों में से एक है क्योंकि रिलेशनल डेटा का सम्मिलन आसान है, जैसे अगर मैं एक इकाई सम्मिलित करता हूं जिसमें पहले से ही रिलेशनल डेटा शामिल है , यह भी सम्मिलित करेगा, क्या आप कभी इस परिदृश्य में आए हैं? धन्यवाद!
बोंगो शार्प

2
दुर्भाग्य से एक DBMS में वस्तुओं का एक वेब सम्मिलित करना वास्तव में ऐसा कुछ नहीं है जिसे बल्ककॉपी करेगा। EF जैसे ORM का लाभ यह है कि लागत को समान रूप से सैकड़ों समान ऑब्जेक्ट ग्राफ़ को कुशलतापूर्वक करने के लिए स्केल नहीं किया जाएगा।
एडम रैकिस

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

49

मैं एडम रेकिस से सहमत हूं। SqlBulkCopyएक डेटा स्रोत से दूसरे में बल्क रिकॉर्ड को स्थानांतरित करने का सबसे तेज़ तरीका है। मैंने इसका उपयोग 20K रिकॉर्ड की नकल करने के लिए किया और इसे 3 सेकंड से भी कम समय लगा। नीचे दिए गए उदाहरण पर एक नज़र डालें।

public static void InsertIntoMembers(DataTable dataTable)
{           
    using (var connection = new SqlConnection(@"data source=;persist security info=True;user id=;password=;initial catalog=;MultipleActiveResultSets=True;App=EntityFramework"))
    {
        SqlTransaction transaction = null;
        connection.Open();
        try
        {
            transaction = connection.BeginTransaction();
            using (var sqlBulkCopy = new SqlBulkCopy(connection, SqlBulkCopyOptions.TableLock, transaction))
            {
                sqlBulkCopy.DestinationTableName = "Members";
                sqlBulkCopy.ColumnMappings.Add("Firstname", "Firstname");
                sqlBulkCopy.ColumnMappings.Add("Lastname", "Lastname");
                sqlBulkCopy.ColumnMappings.Add("DOB", "DOB");
                sqlBulkCopy.ColumnMappings.Add("Gender", "Gender");
                sqlBulkCopy.ColumnMappings.Add("Email", "Email");

                sqlBulkCopy.ColumnMappings.Add("Address1", "Address1");
                sqlBulkCopy.ColumnMappings.Add("Address2", "Address2");
                sqlBulkCopy.ColumnMappings.Add("Address3", "Address3");
                sqlBulkCopy.ColumnMappings.Add("Address4", "Address4");
                sqlBulkCopy.ColumnMappings.Add("Postcode", "Postcode");

                sqlBulkCopy.ColumnMappings.Add("MobileNumber", "MobileNumber");
                sqlBulkCopy.ColumnMappings.Add("TelephoneNumber", "TelephoneNumber");

                sqlBulkCopy.ColumnMappings.Add("Deleted", "Deleted");

                sqlBulkCopy.WriteToServer(dataTable);
            }
            transaction.Commit();
        }
        catch (Exception)
        {
            transaction.Rollback();
        }

    }
}

1
मैंने इस पोस्ट में दिए गए कई समाधानों की कोशिश की और SqlBulkCopy अब तक सबसे तेज था। शुद्ध EF 15min लिया, लेकिन समाधान और SqlBulkCopy के मिश्रण के साथ मैं 1.5 मिनट तक नीचे जाने में सक्षम था! यह 2 मिलियन रिकॉर्ड के साथ था! किसी भी DB सूचकांक अनुकूलन के बिना।
जोनास

डेटाटेबल की तुलना में सूची आसान है। एक AsDataReader()विस्तार विधि है, इस उत्तर में समझाया गया है: stackoverflow.com/a/36817205/1507899
RJB

लेकिन इसकी केवल शीर्ष एंटिटी के लिए संबंधपरक नहीं है
जाहिद मुस्तफा

1
@ ज़ाहिदमुस्तफ़ा: हाँ। यह BulkInsert कर रहा है, बल्क-एनालिसिस-एंड-रिलेशन-ट्रेसिंग-ऑन-ऑब्जेक्ट-ग्राफ़ नहीं .. यदि आप संबंधों को कवर करना चाहते हैं, तो आपको सम्मिलन आदेश का विश्लेषण और निर्धारण करना होगा और फिर व्यक्तिगत स्तरों को थोक-सम्मिलित करना होगा और हो सकता है कि कुछ कुंजियों को अपडेट करें जरूरत है, और आप शीघ्र कस्टम अनुरूप समाधान प्राप्त करेंगे। या, आप ऐसा करने के लिए ईएफ पर भरोसा कर सकते हैं, अपनी तरफ से कोई काम नहीं करते हैं, लेकिन रनटाइम के दौरान धीमी गति से।
quetzalcoatl

23

मैं ईएफ का उपयोग करके थोक आवेषण करने के तरीके पर इस लेख की सिफारिश करूंगा।

इकाई ढांचा और धीमी थोक INSERTs

वह इन क्षेत्रों की खोज करता है और पूर्णता की तुलना करता है:

  1. डिफ़ॉल्ट ईएफ (30,000 रिकॉर्ड जोड़ने के लिए 57 मिनट)
  2. ADO.NET कोड के साथ प्रतिस्थापित करना (25 सेकंड) उन्हीं 30,000 के लिए )
  3. संदर्भ ब्लोट- कार्य की प्रत्येक इकाई के लिए एक नया संदर्भ का उपयोग करके सक्रिय संदर्भ ग्राफ़ को छोटा रखें (वही 30,000 आवेषण 33%)
  4. बड़ी सूची - AutoDetectChangesEnabled बंद करें (लगभग 20 सेकंड के लिए समय नीचे लाता है)
  5. बैचिंग (16 सेकंड तक)
  6. DbTable.AddRange () - (प्रदर्शन 12 श्रेणी में है)

21

जैसा कि यहाँ बताया गया है कि मैं यहाँ EFCore.BulkExtensions को फिर से जोड़ना चाहता हूँ

context.BulkInsert(entitiesList);                 context.BulkInsertAsync(entitiesList);
context.BulkUpdate(entitiesList);                 context.BulkUpdateAsync(entitiesList);
context.BulkDelete(entitiesList);                 context.BulkDeleteAsync(entitiesList);
context.BulkInsertOrUpdate(entitiesList);         context.BulkInsertOrUpdateAsync(entitiesList);         // Upsert
context.BulkInsertOrUpdateOrDelete(entitiesList); context.BulkInsertOrUpdateOrDeleteAsync(entitiesList); // Sync
context.BulkRead(entitiesList);                   context.BulkReadAsync(entitiesList);

1
मैंने यह सुझाव दूसरा दिया। कई homebrew समाधानों की कोशिश करने के बाद इसने 50 सेकंड से 1 सेकंड के लिए मेरी प्रविष्टि को काट दिया। और, यह एमआईटी लाइसेंस शामिल करना इतना आसान है।
साउथशोरैक

क्या यह एफई 6.x
आलोक

यह केवल AddRange का उपयोग करने की तुलना में अधिक प्रदर्शनकारी है यदि यह 10 से अधिक संस्थाएं हैं
जैकल

5
10 000 आवेषण 9 मिनट से 12 सेकंड तक चले गए। यह अधिक ध्यान देने योग्य है!
कॉलिस्टो

2
यदि स्वीकृत उत्तरों को बदलने का कोई तरीका है, तो यह आधुनिक स्वीकृत उत्तर होना चाहिए। और मैं चाहता हूं कि ईएफ टीम ने इसे आउट ऑफ बॉक्स प्रदान किया।
तनवीर बदर

18

मैंने सल्लूमा के उत्तर की जांच की है (जो कि बहुत बढ़िया है, विचार आदमी के लिए धन्यवाद), और मैंने बैच का आकार कम कर दिया है जब तक कि मैंने इष्टतम गति को नहीं मारा है। सुलामा के परिणामों को देखते हुए:

  • कमिटमेंट = 1, फिर से बनाएँ = सही: 10 मिनट से अधिक
  • कमिटमेंट = 10, रीक्रिएट कॉन्टेक्स्ट = सच: 241 सेकंड
  • प्रतिबद्ध = 100, फिर से बनाएँ = सही: 164 सेकंड
  • कमिटमेंट = १०००, रीक्रिएट कॉन्टेक्स्ट = सच: १ ९ १ सेकंड

यह दिखाई दे रहा है कि 1 से 10 और 10 से 100 तक बढ़ने पर गति में वृद्धि होती है, लेकिन 100 से 1000 डालने की गति फिर से गिर रही है।

इसलिए मैंने इस बात पर ध्यान केंद्रित किया है कि जब आप बैच आकार को 10 और 100 के बीच के मूल्य में घटाते हैं तो क्या होता है, और यहां मेरे परिणाम हैं (मैं विभिन्न पंक्ति सामग्री का उपयोग कर रहा हूं, इसलिए मेरा समय अलग-अलग मूल्य का है):

Quantity    | Batch size    | Interval
1000    1   3
10000   1   34
100000  1   368

1000    5   1
10000   5   12
100000  5   133

1000    10  1
10000   10  11
100000  10  101

1000    20  1
10000   20  9
100000  20  92

1000    27  0
10000   27  9
100000  27  92

1000    30  0
10000   30  9
100000  30  92

1000    35  1
10000   35  9
100000  35  94

1000    50  1
10000   50  10
100000  50  106

1000    100 1
10000   100 14
100000  100 141

मेरे परिणामों के आधार पर, वास्तविक आकार बैच आकार के लिए 30 के मूल्य के आसपास है। यह 10 और 100 दोनों से कम है। समस्या है, मुझे नहीं पता कि 30 इष्टतम क्यों है, और न ही मुझे इसके लिए कोई तार्किक स्पष्टीकरण मिल सकता है।


2
मैंने Postrges और शुद्ध SQL के साथ समान पाया (यह SQL पर निर्भर करता है EF पर नहीं) कि 30 इष्टतम है।
कामिल ग्रीव

मेरा अनुभव यह है कि विभिन्न कनेक्शन गति और पंक्ति के आकार के लिए इष्टतम अंतर है। तेज़ कनेक्शन और छोटी पंक्तियों के लिए इष्टतम भी> 200 पंक्तियाँ हो सकती हैं।
जिंग

18

जैसा कि अन्य लोगों ने कहा है कि SqlBulkCopy यह करने का तरीका है यदि आप वास्तव में अच्छा प्रदर्शन चाहते हैं।

यह लागू करने के लिए थोड़ा बोझिल है लेकिन ऐसी लाइब्रेरी हैं जो आपकी मदद कर सकती हैं। वहाँ कुछ बाहर हैं, लेकिन मैं इस बार अपनी खुद की लाइब्रेरी को बेशर्म करूंगा: https://github.com/MikaelEliasson/EntityFramework.Utilities#batch-insert-entities

आपको केवल एक ही कोड की आवश्यकता होगी:

 using (var db = new YourDbContext())
 {
     EFBatchOperation.For(db, db.BlogPosts).InsertAll(list);
 }

तो यह कितना तेज है? यह कहना बहुत कठिन है क्योंकि यह बहुत सारे कारकों, कंप्यूटर के प्रदर्शन, नेटवर्क, ऑब्जेक्ट के आकार आदि पर निर्भर करता है। मैंने जो प्रदर्शन परीक्षण किए हैं, उनसे पता चलता है कि 25k इकाइयां लगभग 10 एस मानक तरीके से डाली जा सकती हैं। स्थानीय यदि आप अपने ईएफ कॉन्फ़िगरेशन का अनुकूलन करते हैं जैसे अन्य उत्तरों में उल्लेख किया गया है। EFUtilities के साथ जिसमें लगभग 300ms लगते हैं। इससे भी अधिक दिलचस्प बात यह है कि मैंने इस विधि का उपयोग करते हुए 15 सेकंड के भीतर लगभग 3 लाख संस्थाओं को बचाया है, औसतन प्रति सेकंड 200k संस्थाएं।

एक समस्या है टोकेकोर्स की अगर आपको रिलेटेड डेटा डालने की जरूरत है। यह उपर्युक्त विधि का उपयोग करके sql सर्वर में प्रभावी ढंग से किया जा सकता है, लेकिन इसके लिए आपको एक Id जनरेशन की रणनीति की आवश्यकता होती है, जो आपको अभिभावकों के लिए ऐप-कोड में id जनरेट करने देती है ताकि आप विदेशी कुंजी सेट कर सकें। यह GUIDs या HiLo आईडी जेनरेशन जैसी किसी चीज़ का उपयोग करके किया जा सकता है।


अच्छा काम करता है। सिंटैक्स हालांकि थोड़ा वर्बोज़ है। यह सोचें कि अगर EFBatchOperationआपके पास DbContextहर स्थिर पद्धति से गुजरने की बजाय एक निर्माणकर्ता है तो बेहतर होगा । के सामान्य संस्करण InsertAllऔर UpdateAllजो स्वचालित रूप से संग्रह को ढूंढते हैं, के समान है DbContext.Set<T>, यह भी अच्छा होगा।
kjbartel

धन्यवाद कहने के लिए बस एक त्वरित टिप्पणी! इस कोड ने मुझे 1.5 सेकंड में 170k रिकॉर्ड बचाने की अनुमति दी! पूरी तरह से किसी भी अन्य विधि है जो मैंने पानी से बाहर की कोशिश की है।
टॉम ग्लेन

@ मिकेल एक मुद्दा पहचान क्षेत्रों के साथ काम कर रहा है। क्या आपके पास अभी तक पहचान सम्मिलित करने का कोई तरीका है?
जो फिलिप्स

1
EntityFramework.BulkInsert के विपरीत, यह पुस्तकालय मुक्त रहा। +1
रुडी

14

Dispose()यदि आप संदर्भ में Add()अन्य प्रीलोडेड संस्थाओं (जैसे नेविगेशन गुण) पर भरोसा करते हैं तो संदर्भ समस्याएं पैदा करता है

समान प्रदर्शन को प्राप्त करने के लिए अपने संदर्भ को छोटा रखने के लिए मैं इसी तरह की अवधारणा का उपयोग करता हूं

लेकिन Dispose()संदर्भ और फिर से बनाने के बजाय , मैं पहले से ही संस्थाओं को अलग कर देता हूंSaveChanges()

public void AddAndSave<TEntity>(List<TEntity> entities) where TEntity : class {

const int CommitCount = 1000; //set your own best performance number here
int currentCount = 0;

while (currentCount < entities.Count())
{
    //make sure it don't commit more than the entities you have
    int commitCount = CommitCount;
    if ((entities.Count - currentCount) < commitCount)
        commitCount = entities.Count - currentCount;

    //e.g. Add entities [ i = 0 to 999, 1000 to 1999, ... , n to n+999... ] to conext
    for (int i = currentCount; i < (currentCount + commitCount); i++)        
        _context.Entry(entities[i]).State = System.Data.EntityState.Added;
        //same as calling _context.Set<TEntity>().Add(entities[i]);       

    //commit entities[n to n+999] to database
    _context.SaveChanges();

    //detach all entities in the context that committed to database
    //so it won't overload the context
    for (int i = currentCount; i < (currentCount + commitCount); i++)
        _context.Entry(entities[i]).State = System.Data.EntityState.Detached;

    currentCount += commitCount;
} }

इसे पकड़ कैच के साथ लपेटें और TrasactionScope()यदि आपको आवश्यकता हो, तो कोड को साफ रखने के लिए उन्हें यहां न दिखाएं


1
इसने Entity Framework 6.0 का उपयोग करके इन्सर्ट (AddRange) को धीमा कर दिया। 20.000 पंक्तियों को सम्मिलित करते हुए लगभग 101 सेकंड से 118 सेकंड तक चला गया।
ए.एच.

1
@ स्टीफन हो: मैं अपने संदर्भ को निपटाने से बचने की कोशिश कर रहा हूं। मैं समझ सकता हूं कि यह संदर्भ को फिर से बनाने की तुलना में धीमा है, लेकिन मैं जानना चाहता हूं कि क्या आपने इस संदर्भ को फिर से बनाने की तुलना में तेजी से पाया है लेकिन एक कमिट सेट के साथ।
शिक्षार्थी

@ लर्नर: मुझे लगता है कि यह संदर्भ को फिर से बनाने की तुलना में तेज था। लेकिन मुझे वास्तव में अब याद नहीं है कि मैंने आखिरी में SqlBulkCopy का उपयोग किया था।
स्टीफन हो

मैंने इस तकनीक का उपयोग करने के लिए समाप्त कर दिया क्योंकि, कुछ अजीब कारण के लिए, कुछ लूप के माध्यम से दूसरी पास पर होने वाली ट्रैकिंग पर छोड़ दिया गया था, भले ही मेरे पास सब कुछ एक उपयोग वक्तव्य में लिपटा हुआ था और यहां तक ​​कि DbContext पर Dispose () भी कहा जाता है । जब मैं संदर्भ (दूसरी पास) पर संदर्भ जोड़ूंगा तो संदर्भ सेट की संख्या केवल एक के बजाय 6 हो जाएगी। अन्य आइटम जो मनमाने ढंग से जोड़े गए थे, पहले से ही लूप के माध्यम से पहले पास में डाले गए थे, इसलिए SaveChanges का कॉल दूसरे पास (स्पष्ट कारणों के लिए) पर विफल हो जाएगा।
हॉलमैनैक

9

मुझे पता है कि यह एक बहुत पुराना प्रश्न है, लेकिन यहां एक व्यक्ति ने कहा कि ईएफ के साथ बल्क इंसर्ट का उपयोग करने के लिए एक विस्तार विधि विकसित की है, और जब मैंने जांच की, तो मुझे पता चला कि लाइब्रेरी की लागत आज (एक डेवलपर के लिए) $ 599 है। हो सकता है कि यह पूरे पुस्तकालय के लिए समझ में आता है, हालांकि सिर्फ थोक के लिए यह बहुत अधिक है।

यहाँ एक बहुत ही सरल विस्तार विधि है जिसे मैंने बनाया है। मैं पहले डेटाबेस के साथ जोड़ी का उपयोग करता हूं (पहले कोड के साथ परीक्षण नहीं किया जाता है, लेकिन मुझे लगता है कि वही काम करता है)। YourEntitiesअपने संदर्भ के नाम के साथ बदलें :

public partial class YourEntities : DbContext
{
    public async Task BulkInsertAllAsync<T>(IEnumerable<T> entities)
    {
        using (var conn = new SqlConnection(Database.Connection.ConnectionString))
        {
            await conn.OpenAsync();

            Type t = typeof(T);

            var bulkCopy = new SqlBulkCopy(conn)
            {
                DestinationTableName = GetTableName(t)
            };

            var table = new DataTable();

            var properties = t.GetProperties().Where(p => p.PropertyType.IsValueType || p.PropertyType == typeof(string));

            foreach (var property in properties)
            {
                Type propertyType = property.PropertyType;
                if (propertyType.IsGenericType &&
                    propertyType.GetGenericTypeDefinition() == typeof(Nullable<>))
                {
                    propertyType = Nullable.GetUnderlyingType(propertyType);
                }

                table.Columns.Add(new DataColumn(property.Name, propertyType));
            }

            foreach (var entity in entities)
            {
                table.Rows.Add(
                    properties.Select(property => property.GetValue(entity, null) ?? DBNull.Value).ToArray());
            }

            bulkCopy.BulkCopyTimeout = 0;
            await bulkCopy.WriteToServerAsync(table);
        }
    }

    public void BulkInsertAll<T>(IEnumerable<T> entities)
    {
        using (var conn = new SqlConnection(Database.Connection.ConnectionString))
        {
            conn.Open();

            Type t = typeof(T);

            var bulkCopy = new SqlBulkCopy(conn)
            {
                DestinationTableName = GetTableName(t)
            };

            var table = new DataTable();

            var properties = t.GetProperties().Where(p => p.PropertyType.IsValueType || p.PropertyType == typeof(string));

            foreach (var property in properties)
            {
                Type propertyType = property.PropertyType;
                if (propertyType.IsGenericType &&
                    propertyType.GetGenericTypeDefinition() == typeof(Nullable<>))
                {
                    propertyType = Nullable.GetUnderlyingType(propertyType);
                }

                table.Columns.Add(new DataColumn(property.Name, propertyType));
            }

            foreach (var entity in entities)
            {
                table.Rows.Add(
                    properties.Select(property => property.GetValue(entity, null) ?? DBNull.Value).ToArray());
            }

            bulkCopy.BulkCopyTimeout = 0;
            bulkCopy.WriteToServer(table);
        }
    }

    public string GetTableName(Type type)
    {
        var metadata = ((IObjectContextAdapter)this).ObjectContext.MetadataWorkspace;
        var objectItemCollection = ((ObjectItemCollection)metadata.GetItemCollection(DataSpace.OSpace));

        var entityType = metadata
                .GetItems<EntityType>(DataSpace.OSpace)
                .Single(e => objectItemCollection.GetClrType(e) == type);

        var entitySet = metadata
            .GetItems<EntityContainer>(DataSpace.CSpace)
            .Single()
            .EntitySets
            .Single(s => s.ElementType.Name == entityType.Name);

        var mapping = metadata.GetItems<EntityContainerMapping>(DataSpace.CSSpace)
                .Single()
                .EntitySetMappings
                .Single(s => s.EntitySet == entitySet);

        var table = mapping
            .EntityTypeMappings.Single()
            .Fragments.Single()
            .StoreEntitySet;

        return (string)table.MetadataProperties["Table"].Value ?? table.Name;
    }
}

आप इसका उपयोग किसी भी संग्रह से कर सकते हैं IEnumerable, जो इस तरह से प्राप्त होता है:

await context.BulkInsertAllAsync(items);

कृपया अपना उदाहरण कोड पूरा करें। जहां बल्कोपी है
सीबकिट

1
यह पहले से ही यहाँ है:await bulkCopy.WriteToServerAsync(table);
गुइलेरमे

शायद मैं स्पष्ट नहीं था, आपके लेखन में, आप सुझाव देते हैं कि आपने एक विस्तार किया है ... जो कि मेरा मतलब है कि कोई तीसरा भाग देय नहीं था, जब वास्तव में दोनों विधियों में SqlBulkCopy lib का उपयोग किया जाता है। यह पूरी तरह से SqlBulkCopy पर निर्भर करता है, जब मैंने पूछा कि बल्कोपी कहां से आती है, इसका एक्सटेंशन लिबास है जिसके ऊपर आपने एक्सटेंशन लिब लिखा है। यहाँ कहने के लिए अधिक समझदारी की इच्छा है कि मैंने SqlBulkCopy का उपयोग कैसे किया।
सीबकिट

async संस्करण में con.OpenAsync का उपयोग करना चाहिए
रॉबर्ट

6

एक संग्रहीत प्रक्रिया का उपयोग करने का प्रयास करें जो उस डेटा का XML प्राप्त करेगा जिसे आप सम्मिलित करना चाहते हैं।


9
यदि आप उन्हें XML के रूप में संग्रहीत नहीं करना चाहते हैं, तो XML के रूप में डेटा पास करना आवश्यक नहीं है। SQL 2008 में आप टेबल वैल्यू पैरामीटर का उपयोग कर सकते हैं।
लादिस्लाव मृका

मैंने इसे स्पष्ट नहीं किया, लेकिन मुझे SQL 2005 का समर्थन करने की भी आवश्यकता है
बोंगो शार्प

4

मैंने ऊपर @Slauma s उदाहरण का एक सामान्य विस्तार किया है;

public static class DataExtensions
{
    public static DbContext AddToContext<T>(this DbContext context, object entity, int count, int commitCount, bool recreateContext, Func<DbContext> contextCreator)
    {
        context.Set(typeof(T)).Add((T)entity);

        if (count % commitCount == 0)
        {
            context.SaveChanges();
            if (recreateContext)
            {
                context.Dispose();
                context = contextCreator.Invoke();
                context.Configuration.AutoDetectChangesEnabled = false;
            }
        }
        return context;
    }
}

उपयोग:

public void AddEntities(List<YourEntity> entities)
{
    using (var transactionScope = new TransactionScope())
    {
        DbContext context = new YourContext();
        int count = 0;
        foreach (var entity in entities)
        {
            ++count;
            context = context.AddToContext<TenancyNote>(entity, count, 100, true,
                () => new YourContext());
        }
        context.SaveChanges();
        transactionScope.Complete();
    }
}

4

मैं एंटिटी फ्रेमवर्क में डालने का सबसे तेज़ तरीका ढूंढ रहा हूं

बल्क इंसर्ट का समर्थन करने वाले कुछ तृतीय-पक्ष पुस्तकालय उपलब्ध हैं:

  • Z.EntityFramework.Extensions ( अनुशंसित )
  • EFUtilities
  • EntityFramework.BulkInsert

देखें: एंटिटी फ्रेमवर्क बल्क इंसर्ट लाइब्रेरी

बल्क इंसर्ट लाइब्रेरी चुनते समय सावधान रहें। केवल एंटिटी फ्रेमवर्क एक्सटेंशन सभी प्रकार के संघों और विरासतों का समर्थन करता है और यह अभी भी समर्थित एकमात्र है।


अस्वीकरण : मैं एंटिटी फ्रेमवर्क एक्सटेंशन का मालिक हूं

यह लाइब्रेरी आपको अपने परिदृश्यों के लिए आवश्यक सभी बल्क ऑपरेशन करने की अनुमति देती है:

  • बल्क सेवचेंजेस
  • थोक डालें
  • बल्क डिलीट करें
  • थोक अद्यतन
  • बल्क मर्ज

उदाहरण

// Easy to use
context.BulkSaveChanges();

// Easy to customize
context.BulkSaveChanges(bulk => bulk.BatchSize = 100);

// Perform Bulk Operations
context.BulkDelete(customers);
context.BulkInsert(customers);
context.BulkUpdate(customers);

// Customize Primary Key
context.BulkMerge(customers, operation => {
   operation.ColumnPrimaryKeyExpression = 
        customer => customer.Code;
});

19
यह एक महान विस्तार है, लेकिन मुफ्त नहीं है
ओकेन कोकिजीत

2
यह उत्तर बहुत अच्छा है और EntityFramework.BulkInsert है 1.5 सेकंड में 15K पंक्तियों का एक थोक सम्मिलन करता है, विंडोज सेवा जैसी आंतरिक प्रक्रिया के लिए बहुत अच्छा काम करता है।
पादरी कोरटस

4
हाँ, थोक डालने के लिए 600 $। कुल मूल्य।
इकोनॉन

1
यदि आप इसे comercially उपयोग करते हैं तो @eocron येट इसके लायक है। मुझे किसी चीज़ के लिए $ 600 की कोई समस्या नहीं दिखती है, जो मुझे खुद इसे बनाने में घंटों बिताने की ज़रूरत नहीं है, जिसकी कीमत मुझे $ 600 से बहुत अधिक होगी। हाँ, यह पैसा खर्च करता है लेकिन मेरी प्रति घंटा दर को देखते हुए यह पैसा अच्छी तरह से खर्च होता है!
जॉर्डन वैन आइजक

3

उपयोग करें SqlBulkCopy:

void BulkInsert(GpsReceiverTrack[] gpsReceiverTracks)
{
    if (gpsReceiverTracks == null)
    {
        throw new ArgumentNullException(nameof(gpsReceiverTracks));
    }

    DataTable dataTable = new DataTable("GpsReceiverTracks");
    dataTable.Columns.Add("ID", typeof(int));
    dataTable.Columns.Add("DownloadedTrackID", typeof(int));
    dataTable.Columns.Add("Time", typeof(TimeSpan));
    dataTable.Columns.Add("Latitude", typeof(double));
    dataTable.Columns.Add("Longitude", typeof(double));
    dataTable.Columns.Add("Altitude", typeof(double));

    for (int i = 0; i < gpsReceiverTracks.Length; i++)
    {
        dataTable.Rows.Add
        (
            new object[]
            {
                    gpsReceiverTracks[i].ID,
                    gpsReceiverTracks[i].DownloadedTrackID,
                    gpsReceiverTracks[i].Time,
                    gpsReceiverTracks[i].Latitude,
                    gpsReceiverTracks[i].Longitude,
                    gpsReceiverTracks[i].Altitude
            }
        );
    }

    string connectionString = (new TeamTrackerEntities()).Database.Connection.ConnectionString;
    using (var connection = new SqlConnection(connectionString))
    {
        connection.Open();
        using (var transaction = connection.BeginTransaction())
        {
            using (var sqlBulkCopy = new SqlBulkCopy(connection, SqlBulkCopyOptions.TableLock, transaction))
            {
                sqlBulkCopy.DestinationTableName = dataTable.TableName;
                foreach (DataColumn column in dataTable.Columns)
                {
                    sqlBulkCopy.ColumnMappings.Add(column.ColumnName, column.ColumnName);
                }

                sqlBulkCopy.WriteToServer(dataTable);
            }
            transaction.Commit();
        }
    }

    return;
}

3

किसी सूची को सहेजने के सबसे तेज़ तरीकों में से एक आपको निम्नलिखित कोड लागू करना होगा

context.Configuration.AutoDetectChangesEnabled = false;
context.Configuration.ValidateOnSaveEnabled = false;

AutoDetectChangesEnabled = false

जोड़ें, AddRange और SaveChanges: परिवर्तनों का पता नहीं लगाता है।

वैलिडेटऑनसेव सक्षम = गलत;

परिवर्तन ट्रैकर का पता नहीं लगाता है

आपको नगेट जोड़ना होगा

Install-Package Z.EntityFramework.Extensions

अब आप निम्न कोड का उपयोग कर सकते हैं

var context = new MyContext();

context.Configuration.AutoDetectChangesEnabled = false;
context.Configuration.ValidateOnSaveEnabled = false;

context.BulkInsert(list);
context.BulkSaveChanges();

क्या मैं बल्क अपडेट के लिए आपके सैंपल कोड का उपयोग कर सकता हूं?
अमीनगोलमहल 11

4
Z लाइब्रेरी फ्री नहीं है
SHADOW.NET

3

SqlBulkCopy सुपर त्वरित है

यह मेरा कार्यान्वयन है:

// at some point in my calling code, I will call:
var myDataTable = CreateMyDataTable();
myDataTable.Rows.Add(Guid.NewGuid,tableHeaderId,theName,theValue); // e.g. - need this call for each row to insert

var efConnectionString = ConfigurationManager.ConnectionStrings["MyWebConfigEfConnection"].ConnectionString;
var efConnectionStringBuilder = new EntityConnectionStringBuilder(efConnectionString);
var connectionString = efConnectionStringBuilder.ProviderConnectionString;
BulkInsert(connectionString, myDataTable);

private DataTable CreateMyDataTable()
{
    var myDataTable = new DataTable { TableName = "MyTable"};
// this table has an identity column - don't need to specify that
    myDataTable.Columns.Add("MyTableRecordGuid", typeof(Guid));
    myDataTable.Columns.Add("MyTableHeaderId", typeof(int));
    myDataTable.Columns.Add("ColumnName", typeof(string));
    myDataTable.Columns.Add("ColumnValue", typeof(string));
    return myDataTable;
}

private void BulkInsert(string connectionString, DataTable dataTable)
{
    using (var connection = new SqlConnection(connectionString))
    {
        connection.Open();
        SqlTransaction transaction = null;
        try
        {
            transaction = connection.BeginTransaction();

            using (var sqlBulkCopy = new SqlBulkCopy(connection, SqlBulkCopyOptions.TableLock, transaction))
            {
                sqlBulkCopy.DestinationTableName = dataTable.TableName;
                foreach (DataColumn column in dataTable.Columns) {
                    sqlBulkCopy.ColumnMappings.Add(column.ColumnName, column.ColumnName);
                }

                sqlBulkCopy.WriteToServer(dataTable);
            }
            transaction.Commit();
        }
        catch (Exception)
        {
            transaction?.Rollback();
            throw;
        }
    }
}

3

[2019 अपडेट] ईएफ कोर 3.1

ऊपर जो कहा गया है, उसके बाद EF Core में AutoDetectChangesEnabled को अक्षम करना पूरी तरह से काम करता है: सम्मिलन का समय 100 से विभाजित किया गया था (कई मिनट से कुछ सेकंड तक, क्रॉस टेबल संबंधों के साथ 10k रिकॉर्ड)

अद्यतन कोड है:

  context.ChangeTracker.AutoDetectChangesEnabled = false;
            foreach (IRecord record in records) {
               //Add records to your database        
            }
            context.ChangeTracker.DetectChanges();
            context.SaveChanges();
            context.ChangeTracker.AutoDetectChangesEnabled = true; //do not forget to re-enable

2

यहाँ एक फ्रेमवर्क एंटिटी फ्रेमवर्क का उपयोग करने और वास्तविक उदाहरण पर SqlBulkCopy वर्ग का उपयोग करने के बीच एक तुलना है: SQL सर्वर डेटाबेस में जटिल ऑब्जेक्ट सम्मिलित करने के कैसे करें

जैसा कि दूसरों ने पहले ही जोर दिया था, ओआरएम का इस्तेमाल थोक परिचालन में करने के लिए नहीं होता है। वे लचीलापन, चिंताओं को अलग करने और अन्य लाभों की पेशकश करते हैं, लेकिन थोक संचालन (थोक पढ़ने को छोड़कर) उनमें से एक नहीं है।


2

एक अन्य विकल्प Nuget से उपलब्ध SqlBulkTools का उपयोग करना है। इसका उपयोग करना बहुत आसान है और इसमें कुछ शक्तिशाली विशेषताएं हैं।

उदाहरण:

var bulk = new BulkOperations();
var books = GetBooks();

using (TransactionScope trans = new TransactionScope())
{
    using (SqlConnection conn = new SqlConnection(ConfigurationManager
    .ConnectionStrings["SqlBulkToolsTest"].ConnectionString))
    {
        bulk.Setup<Book>()
            .ForCollection(books)
            .WithTable("Books") 
            .AddAllColumns()
            .BulkInsert()
            .Commit(conn);
    }

    trans.Complete();
}

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


2
यह परियोजना NuGet और GitHub दोनों से हटा दी गई है।
0x

1

मेरी जानकारी के अनुसार वहाँ no BulkInsertमेंEntityFramework विशाल आवेषण के प्रदर्शन को बढ़ाने के लिए।

इस परिदृश्य में आप के साथ जा सकते हैं SqlBulkCopy में ADO.netअपनी समस्या को हल करने के लिए


मैं उस वर्ग पर एक नज़र डाल रहा था, लेकिन यह टेबल-टू-टेबल सम्मिलन के लिए अधिक उन्मुख प्रतीत होता है, है न?
बोंगो शार्प

यकीन नहीं है कि आप क्या मतलब है, यह एक अतिभारित है WriteToServerकि एक लेता है DataTable
ब्लाइंडी

नहीं, आप .net ऑब्जेक्ट्स से SQL में भी डाल सकते हैं। आप क्या देख रहे हैं?
अनीशमरोकी

डेटाबेस में ट्रांसेशनस्कोप ब्लॉक में संभावित रूप से हजारों रिकॉर्ड डालने का एक तरीका
बोंगो शार्प


1

क्या आपने कभी पृष्ठभूमि कार्यकर्ता या कार्य के माध्यम से सम्मिलित करने की कोशिश की है?

मेरे मामले में, 7760 रजिस्टरों को सम्मिलित करते हुए, 182 अलग-अलग तालिकाओं में विदेशी कुंजी रिश्तों (नेविगेशनप्रोपरेटी द्वारा) के साथ वितरित किया गया।

कार्य के बिना, इसमें 2 मिनट और आधा समय लगा। टास्क ( Task.Factory.StartNew(...)) के भीतर , इसमें 15 सेकंड लगे।

Im केवल SaveChanges()सभी संस्थाओं को संदर्भ में जोड़ने के बाद कर रहा हूँ । (डेटा अखंडता सुनिश्चित करने के लिए)


2
मुझे पूरा यकीन है कि संदर्भ थ्रेड सुरक्षित नहीं है। क्या आपके पास यह सुनिश्चित करने के लिए परीक्षण हैं कि सभी निकाय बच गए थे?
डैनी व्रॉड

मुझे पता है कि पूरी इकाई का ढांचा बिल्कुल भी सुरक्षित नहीं है, लेकिन केवल संदर्भ में वस्तुओं को जोड़ना और अंत में सहेजना है ... इसका काम यहां पूरी तरह से करना है।
राफेल AMS

तो, आप DbContext.SaveChanges () को मुख्य थ्रेड में बुला रहे हैं, लेकिन संदर्भ में संस्थाओं को जोड़ना बैकग्राउंड थ्रेड में किया जाता है, है ना?
प्रोक्योरर्स

1
हां, थ्रेड्स के अंदर डेटा जोड़ें; सब खत्म होने का इंतज़ार करो; और मुख्य सूत्र में परिवर्तन सहेजें
राफेल AMS

हालांकि मुझे लगता है कि यह तरीका खतरनाक है और गलतियों से ग्रस्त है, मुझे यह बहुत दिलचस्प लगता है।
शिक्षार्थी

1

यहां लिखे गए सभी समाधान मदद नहीं करते हैं क्योंकि जब आप SaveChanges () करते हैं, तो सम्मिलित विवरण एक-एक करके डेटाबेस में भेजे जाते हैं, यही एंटोन काम करता है।

और अगर आपकी डेटाबेस और बैक की यात्रा उदाहरण के लिए 50 एमएस है, तो डालने के लिए आवश्यक समय रिकॉर्ड x 50 एमएस की संख्या है।

आपको BulkInsert का उपयोग करना है, यहाँ लिंक है: https://efbulkinsert.codeplex.com/

मुझे इसके उपयोग से 5-6 मिनट से लेकर 10-12 सेकंड तक कम करने का समय मिला।


1

आप थोक पैकेज का उपयोग कर सकते हैं लाइब्रेरी का । बल्क इन्सर्ट 1.0.0 संस्करण का उपयोग एंटिटी फ्रेमवर्क> = 6.0.0 वाले प्रोजेक्ट में किया जाता है।

अधिक विवरण यहां पाया जा सकता है- बल्कॉपरेशन स्रोत कोड


1

[POSTGRESQL के लिए नई समाधान] अरे, मुझे पता है कि यह काफी पुरानी पोस्ट है, लेकिन मैं हाल ही में इसी तरह की समस्या में चला गया हूं, लेकिन हम Postgresql का उपयोग कर रहे थे। मैं प्रभावी bulkinsert का उपयोग करना चाहता था, जो कि बहुत कठिन निकला। मुझे इस डीबी पर ऐसा करने के लिए कोई उचित मुफ्त पुस्तकालय नहीं मिला है। मुझे केवल यह सहायक मिला है: https://bytefish.de/blog/postgresql_bulk_insert/ जो नुगेट पर भी है। मैंने एक छोटा सा मैपर लिखा है, जो ऑटो ने एंटिटी फ्रेमवर्क के तरीके को मैप किया:

public static PostgreSQLCopyHelper<T> CreateHelper<T>(string schemaName, string tableName)
        {
            var helper = new PostgreSQLCopyHelper<T>("dbo", "\"" + tableName + "\"");
            var properties = typeof(T).GetProperties();
            foreach(var prop in properties)
            {
                var type = prop.PropertyType;
                if (Attribute.IsDefined(prop, typeof(KeyAttribute)) || Attribute.IsDefined(prop, typeof(ForeignKeyAttribute)))
                    continue;
                switch (type)
                {
                    case Type intType when intType == typeof(int) || intType == typeof(int?):
                        {
                            helper = helper.MapInteger("\"" + prop.Name + "\"",  x => (int?)typeof(T).GetProperty(prop.Name).GetValue(x, null));
                            break;
                        }
                    case Type stringType when stringType == typeof(string):
                        {
                            helper = helper.MapText("\"" + prop.Name + "\"", x => (string)typeof(T).GetProperty(prop.Name).GetValue(x, null));
                            break;
                        }
                    case Type dateType when dateType == typeof(DateTime) || dateType == typeof(DateTime?):
                        {
                            helper = helper.MapTimeStamp("\"" + prop.Name + "\"", x => (DateTime?)typeof(T).GetProperty(prop.Name).GetValue(x, null));
                            break;
                        }
                    case Type decimalType when decimalType == typeof(decimal) || decimalType == typeof(decimal?):
                        {
                            helper = helper.MapMoney("\"" + prop.Name + "\"", x => (decimal?)typeof(T).GetProperty(prop.Name).GetValue(x, null));
                            break;
                        }
                    case Type doubleType when doubleType == typeof(double) || doubleType == typeof(double?):
                        {
                            helper = helper.MapDouble("\"" + prop.Name + "\"", x => (double?)typeof(T).GetProperty(prop.Name).GetValue(x, null));
                            break;
                        }
                    case Type floatType when floatType == typeof(float) || floatType == typeof(float?):
                        {
                            helper = helper.MapReal("\"" + prop.Name + "\"", x => (float?)typeof(T).GetProperty(prop.Name).GetValue(x, null));
                            break;
                        }
                    case Type guidType when guidType == typeof(Guid):
                        {
                            helper = helper.MapUUID("\"" + prop.Name + "\"", x => (Guid)typeof(T).GetProperty(prop.Name).GetValue(x, null));
                            break;
                        }
                }
            }
            return helper;
        }

मैं इसे निम्नलिखित तरीके से उपयोग करता हूं (मेरे पास अंडरटेकिंग नाम की संस्था थी):

var undertakingHelper = BulkMapper.CreateHelper<Model.Undertaking>("dbo", nameof(Model.Undertaking));
undertakingHelper.SaveAll(transaction.UnderlyingTransaction.Connection as Npgsql.NpgsqlConnection, undertakingsToAdd));

मैंने लेन-देन के साथ एक उदाहरण दिखाया, लेकिन यह संदर्भ से प्राप्त सामान्य कनेक्शन के साथ भी किया जा सकता है। undertakingsToAdd सामान्य इकाई रिकॉर्ड्स के योग्य है, जिसे मैं DB में थोक करना चाहता हूं।

यह समाधान, जिसे मैंने कुछ घंटों के शोध और कोशिश के बाद प्राप्त किया है, जैसा कि आप बहुत तेजी से और अंत में उपयोग करने और मुक्त होने की उम्मीद कर सकते हैं! मैं वास्तव में आपको इस समाधान का उपयोग करने की सलाह देता हूं, न केवल ऊपर बताए गए कारणों के लिए, बल्कि इसलिए भी कि यह एकमात्र ऐसा है जिसके साथ मुझे पोस्टग्रैसेकल के साथ कोई समस्या नहीं थी, कई अन्य समाधान SqlServer के साथ उदाहरण के लिए निर्दोष रूप से काम करते हैं।


0

रहस्य एक समान खाली मंचन तालिका में सम्मिलित करना है। आवक तेज हो रही है। फिर उसमे से अपनी मुख्य बड़ी तालिका में एक सम्मिलित सम्मिलित करें। फिर अगले बैच के लिए तैयार मचान तालिका को काटें।

अर्थात।

insert into some_staging_table using Entity Framework.

-- Single insert into main table (this could be a tiny stored proc call)
insert into some_main_already_large_table (columns...)
   select (columns...) from some_staging_table
truncate table some_staging_table

EF का उपयोग करके, अपने सभी रिकॉर्ड्स को एक खाली स्टेजिंग टेबल में जोड़ें। तब SQL का उपयोग मुख्य (बड़ी और धीमी) तालिका में एक एकल SQL निर्देश में सम्मिलित करने के लिए करें। फिर अपनी स्टेजिंग टेबल को खाली करें। यह पहले से ही बड़ी तालिका में बहुत अधिक डेटा डालने का एक बहुत तेज़ तरीका है।
साइमन ह्यूजेस

13
जब आप कहते हैं कि ईएफ का उपयोग करते हुए, स्टैजिंग टेबल में रिकॉर्ड जोड़ें, क्या आपने वास्तव में ईएफ के साथ यह कोशिश की थी? चूंकि EF डेटाबेस में प्रत्येक इंसर्ट के साथ एक अलग कॉल जारी करता है, मुझे संदेह है कि आप वही पूर्ण हिट देखने जा रहे हैं जिससे ओपी बचने की कोशिश कर रहा है। मंचन तालिका इस समस्या से कैसे बचती है?
जिम वूले जूल 17'13

-1

लेकिन, (+4000) से अधिक आवेषण के लिए मैं संग्रहीत प्रक्रिया का उपयोग करने की सलाह देता हूं। संलग्न समय व्यतीत हो गया। मैंने इसे 20 में 11.788 पंक्तियाँ डालीं ”यहाँ छवि विवरण दर्ज करें

यह कोड है

 public void InsertDataBase(MyEntity entity)
    {
        repository.Database.ExecuteSqlCommand("sp_mystored " +
                "@param1, @param2"
                 new SqlParameter("@param1", entity.property1),
                 new SqlParameter("@param2", entity.property2));
    }

-1

संग्रहीत प्रक्रिया का उपयोग करें जो डेटा डालने के लिए xml के रूप में इनपुट डेटा लेता है।

अपने सी # कोड से एक्सएमएल के रूप में डेटा डालें।

जैसे कि c # में, सिंटैक्स इस तरह होगा:

object id_application = db.ExecuteScalar("procSaveApplication", xml)

-7

एंटिटी फ्रेमवर्क में रिकॉर्ड डालने की गति बढ़ाने के लिए इस तकनीक का उपयोग करें। यहाँ मैं अभिलेखों को सम्मिलित करने के लिए एक सरल संग्रहित प्रक्रिया का उपयोग करता हूँ। और इस संग्रहीत कार्यविधि को कार्यान्वित करने के लिए मैं। फ्रेमवर्क () एंटिटी फ्रेमवर्क की विधि का उपयोग करता हूं । जो रॉ एसक्यूएल निष्पादित करता है।

संग्रहीत कार्यविधि कोड:

CREATE PROCEDURE TestProc
@FirstParam VARCHAR(50),
@SecondParam VARCHAR(50)

AS
  Insert into SomeTable(Name, Address) values(@FirstParam, @SecondParam) 
GO

अगला, अपने सभी 4000 रिकॉर्ड के माध्यम से लूप और एंटिटी फ्रेमवर्क कोड जोड़ें, जो संग्रहीत को निष्पादित करता है

प्रक्रिया हर 100 वें लूप में प्याज होती है।

इसके लिए मैं इस प्रक्रिया को निष्पादित करने के लिए एक स्ट्रिंग क्वेरी बनाता हूं, इसे रिकॉर्ड के हर सेट पर जोड़कर रखता हूं।

फिर यह जांचें कि लूप 100 के गुणकों में चल रहा है और उस स्थिति में इसका उपयोग करके निष्पादित करें .FromSql()

तो 4000 रिकॉर्ड के लिए मुझे केवल 4000/100 = 40 बार प्रक्रिया को निष्पादित करना होगा ।

नीचे दिए गए कोड की जाँच करें:

string execQuery = "";
var context = new MyContext();
for (int i = 0; i < 4000; i++)
{
    execQuery += "EXEC TestProc @FirstParam = 'First'" + i + "'', @SecondParam = 'Second'" + i + "''";

    if (i % 100 == 0)
    {
        context.Student.FromSql(execQuery);
        execQuery = "";
    }
}

यह कुशल हो सकता है लेकिन इकाई ढांचे का उपयोग नहीं करने के बराबर है। ओपी का सवाल था कि एंटिटी फ्रेमवर्क के संदर्भ में दक्षता को अधिकतम कैसे किया जाए
kall2sollies
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.