2012-10-05 21 views
40

(इकाई फ्रेमवर्क) EF5 का उपयोग करके बैच अपडेट से निपटने का सबसे अच्छा तरीका क्या है? 100 और 100,000 के बीच ईद की है, जो प्राथमिक कुंजी की एक सूची (सूची) के लिए एक क्षेत्र (जैसे अद्यतन किया) अपडेट कर रहा हैबैच अपडेट/हटाएं EF5

  1. : मैं 2 विशेष मामलों मैं में दिलचस्पी रखता हूँ है। प्रत्येक अपडेट को अलग-अलग कॉल करना बहुत अधिक ओवरहेड लगता है और इसमें काफी समय लगता है।

  2. एक ही वस्तु में एक ही वस्तु (जैसे उपयोगकर्ता) के 100 और 100.000 के बीच, कई को सम्मिलित करना।

कोई अच्छी सलाह?

उत्तर

35
  1. दो खुली स्रोत परियोजनाएं हैं जो इसे अनुमति देती हैं: EntityFramework.Extended और Entity Framework Extensions। आप ईएफ की कोडप्लेक्स साइट पर थोक अपडेट के बारे में discussion भी देख सकते हैं।
  2. ईएफ के माध्यम से 100k रिकॉर्ड डालने पर पहली जगह गलत एप्लिकेशन आर्किटेक्चर है। आपको डेटा आयात के लिए अलग-अलग हल्के तकनीक का चयन करना चाहिए। इस तरह के बड़े रिकॉर्ड सेट के साथ भी ईएफ के आंतरिक ऑपरेशन से आपको बहुत अधिक प्रोसेसिंग समय लगेगा। वर्तमान में ईएफ के लिए बैच आवेषण के लिए कोई समाधान नहीं है लेकिन ईएफ के कोड प्लेक्स साइट पर इस सुविधा के बारे में broad discussion है।

    1:

+1

आप EntityFramework.Extended में एफई v4 का समर्थन करने का पता है? और ऐसा लगता है कि इकाई फ्रेमवर्क एक्सटेंशन मृत परियोजना है (अंतिम रिलीज 2010 में थी) –

+1

कोडप्लेक्स पर ef थोक आवेषण के लिए एक एक्सटेंशन है http://stackoverflow.com/a/27983392/937411 –

+0

बैचिंग अभी भी महत्वपूर्ण है? E126 में – Jordan

3

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

भारी प्रविष्टियों के लिए, आपकी सबसे अच्छी शर्त थोक प्रतिलिपि या एसएसआईएस का उपयोग करना है। ईएफ को प्रत्येक पंक्ति डालने के लिए डेटाबेस में एक अलग हिट की आवश्यकता होगी।

21

मैं निम्नलिखित विकल्प देखें। सबसे आसान तरीका है - हाथ से अपने एसक्यूएल अनुरोध बनाने और के माध्यम से ObjectContext.ExecuteStoreCommand

context.ExecuteStoreCommand("UPDATE TABLE SET FIELD1 = {0} WHERE FIELD2 = {1}", value1, value2); 

2 निष्पादित। EntityFramework.Extended

context.Tasks.Update(
    t => t.StatusId == 1, 
    t => new Task {StatusId = 2}); 

3 का उपयोग करें। ईएफ के लिए अपना खुद का विस्तार करें। एक लेख Bulk Delete है जहां यह लक्ष्य ऑब्जेक्ट कॉन्टेक्स्ट कक्षा विरासत में प्राप्त किया गया था। एक नज़र रखना लायक है। थोक डालने/अद्यतन उसी तरह लागू किया जा सकता है।

+1

'bContext.Database.ExecuteSqlCommand()'? – Jess

+0

इसके बजाय ctx.Database.ExecuteSqlCommand – Ovis

2

एसक्यूएलकल्क कॉपी क्लास का उपयोग करके थोक आवेषण किया जाना चाहिए। कृपया दो एकीकरण पर पहले से मौजूद StackOverflow क्यू & एक देखें: SqlBulkCopy and Entity Framework

SqlBulkCopy एक बहुत अधिक उपयोगकर्ता के अनुकूल BCP से (बल्क कॉपी आदेश-पंक्ति उपयोगिता) या यहां तक ​​कि खुली rowset है।

+0

का उपयोग करें SqlBulkCopy के साथ थोक अद्यतन जैसी कोई चीज़ नहीं है, केवल BulkInsert – Pleun

+0

मैं शायद प्रश्न के दूसरे भाग का जवाब दे रहा था, और गलती से "थोक प्रविष्टियों" के बजाय "थोक अपडेट" टाइप किया। फिक्स्ड। –

0

मैं स्वीकार किए गए उत्तर से सहमत हूं कि एफई शायद थोक आवेषण के लिए गलत तकनीक है। हालांकि, मुझे लगता है कि EntityFramework.BulkInsert पर एक नज़र डालने लायक है।

0

यहाँ मैं सफलतापूर्वक क्या किया है:

private void BulkUpdate() 
{ 
    var oc = ((IObjectContextAdapter)_dbContext).ObjectContext; 
    var updateQuery = myIQueryable.ToString(); // This MUST be above the call to get the parameters. 
    var updateParams = GetSqlParametersForIQueryable(updateQuery).ToArray(); 
    var updateSql = [email protected]"UPDATE dbo.myTable 
         SET col1 = x.alias2 
         FROM dbo.myTable 
         JOIN ({updateQuery}) x(alias1, alias2) ON x.alias1 = dbo.myTable.Id"; 
    oc.ExecuteStoreCommand(updateSql, updateParams); 
} 

private void BulkInsert() 
{ 
    var oc = ((IObjectContextAdapter)_dbContext).ObjectContext; 
    var insertQuery = myIQueryable.ToString(); // This MUST be above the call to get the parameters. 
    var insertParams = GetSqlParametersForIQueryable(insertQuery).ToArray(); 
    var insertSql = [email protected]"INSERT INTO dbo.myTable (col1, col2) 
         SELECT x.alias1, x.alias2 
         FROM ({insertQuery}) x(alias1, alias2)"; 
    oc.ExecuteStoreCommand(insertSql, insertParams.ToArray()); 
}  

private static IEnumerable<SqlParameter> GetSqlParametersForIQueryable<T>(IQueryable<T> queryable) 
{ 
    var objectQuery = GetObjectQueryFromIQueryable(queryable); 
    return objectQuery.Parameters.Select(x => new SqlParameter(x.Name, x.Value)); 
} 

private static ObjectQuery<T> GetObjectQueryFromIQueryable<T>(IQueryable<T> queryable) 
{ 
    var dbQuery = (DbQuery<T>)queryable; 
    var iqProp = dbQuery.GetType().GetProperty("InternalQuery", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public); 
    var iq = iqProp.GetValue(dbQuery, null); 
    var oqProp = iq.GetType().GetProperty("ObjectQuery", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public); 
    return (ObjectQuery<T>)oqProp.GetValue(iq, null); 
} 
0
public static bool BulkDelete(string tableName, string columnName, List<object> val) 
    { 
     bool ret = true; 

     var max = 2000; 
     var pages = Math.Ceiling((double)val.Count/max); 
     for (int i = 0; i < pages; i++) 
     { 
      var count = max; 
      if (i == pages - 1) { count = val.Count % max; } 

      var args = val.GetRange(i * max, count); 
      var cond = string.Join("", args.Select((t, index) => $",@p{index}")).Substring(1); 
      var sql = $"DELETE FROM {tableName} WHERE {columnName} IN ({cond}) "; 

      ret &= Db.ExecuteSqlCommand(sql, args.ToArray()) > 0; 
     } 

     return ret; 
    }