2013-08-07 8 views
17

के साथ डुप्लिकेट कुंजी डालने को अनदेखा करें मैं एंटिटी फ्रेमवर्क कोड प्रथम के साथ एएसपी.नेट एमवीसी 4 का उपयोग कर रहा हूं। मेरे पास "उपयोगकर्ता" नामक एक तालिका है, प्राथमिक कुंजी "UserId" के साथ। इस तालिका में 200,000+ प्रविष्टियां हो सकती हैं।इकाई फ्रेमवर्क

मुझे अन्य 50 उपयोगकर्ताओं को सम्मिलित करने की आवश्यकता है। मैं इसे

foreach(User user in NewUsers){ 
    context.Add(user); 
} 
dbcontext.SaveChanges(); 

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

foreach(User user in NewUsers){ 
    if(dbcontext.Users.FirstOrDefault(u => u.UserId) == null) 
    { 
     dbcontext.Users.Add(user); 
    } 
} 
dbcontext.SaveChanges(); 

जो काम करेगा। समस्या यह है कि, उसे 200,000+ प्रविष्टि तालिका पर 50 बार एक क्वेरी चलाने की आवश्यकता है। तो मेरा सवाल यह है कि, इन उपयोगकर्ताओं को डालने का सबसे प्रदर्शन करने योग्य तरीका क्या है, किसी भी डुप्लिकेट को अनदेखा कर रहा है?

+2

शायद 'context.AddOrUpdate (उपयोगकर्ता);' क्या आप –

+1

@OO चाहते यह वास्तव में उन नहीं है, यह एक एपीआई से खींचा जा रहा अलग डेटा। एपीआई एकाधिक अनुक्रमिक कॉल में एक ही डेटा प्रदान कर सकता है या नहीं। मैंने अभी "उपयोगकर्ता" का उपयोग किया क्योंकि यह पहला उदाहरण था जिसे मैंने सोचा था। – Jordan

+1

मुझे एक ही समस्या थी और मुझे पर्याप्त समाधान नहीं मिला। इसके लिए मान्य परिदृश्य हैं, जैसे कि सीएसवी से थोक आयात करते समय जहां ईमेल पते को मौजूदा डेटाबेस में अद्वितीय होना आवश्यक है। स्मृति में सभी मौजूदा कुंजियों को पढ़ना प्रदर्शन के लिए अच्छा प्रतीत नहीं होता है, न ही प्रत्येक प्रविष्टि को अलग से जोड़ रहा है। ऐसा लगता है कि इन्सर्ट इग्नोर की तरह कुछ जरूरी है। – acarlon

उत्तर

6

आप ऐसा कर सकते हैं:

var newUserIDs = NewUsers.Select(u => u.UserId).Distinct().ToArray(); 
var usersInDb = dbcontext.Users.Where(u => newUserIDs.Contains(u.UserId)) 
           .Select(u => u.UserId).ToArray(); 
var usersNotInDb = NewUsers.Where(u => !usersInDb.Contains(u.UserId)); 
foreach(User user in usersNotInDb){ 
    context.Add(user); 
} 

dbcontext.SaveChanges(); 

यह आपके डेटाबेस में किसी एक क्वेरी उन जो पहले से ही मौजूद खोजने के लिए निष्पादित फिर उन्हें अपने NewUsers सेट से बाहर फिल्टर करेगा।

के रूप में टिप्पणी प्रस्ताव ऊपर NewUsers संग्रह में प्रत्येक तत्व के लिए एक एसक्यूएल कॉल का परिणाम देगा में बताया:

+0

बड़े संग्रह के लिए, क्या Toorray() के बजाय हैशसेट्स का उपयोग करना बेहतर होगा? – tbmsu

+1

@tbmsu इकाई फ्रेमवर्क कॉल के लिए, शायद इसका कोई प्रदर्शन प्रभाव नहीं होगा क्योंकि 'कंटेनस' वास्तव में SQL 'IN' खंड में अनुवादित हो जाता है, इसलिए यह वास्तव में किसी और चीज़ से डीबी प्रदर्शन पर निर्भर करता है। यह भी ध्यान रखें कि * बड़े * डेटा सेट के लिए, यह शायद बिल्कुल काम नहीं करेगा क्योंकि 'IN' खंड ([ref] (http://stackoverflow.com/questions) में उपयोग किए जाने वाले मानों की संख्या की सीमा है/1069415/सीमा-ऑन-द-जहां col-इन-हालत))। –

2

चूंकि यह आपकी प्राथमिक कुंजी है, इसलिए आपके विकल्प सीमित हैं। यदि यह आपकी प्राथमिक कुंजी नहीं थी, और केवल एक अद्वितीय अनुक्रमणिका है, तो SQL सर्वर मानते हुए, आप डुप्लिकेट को अनदेखा करने के लिए अपनी अनूठी कुंजी सेट अप कर सकते हैं।

मैं जो सुझाव दे सकता हूं वह है कि अपवाद को डुप्लिकेट कुंजी त्रुटि अगर अपवाद जोड़ें और अपवाद खाएं।

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

मुझे लगता है कि यह मैं था, तो मैं कोशिश/पकड़ मार्ग पर जाऊंगा।

+0

तो आप कह रहे हैं कि SaveChanges() को फॉर-लूप में इसके चारों ओर एक कोशिश/पकड़ के साथ रखें। क्या यह 50 अलग-अलग SaveChanges() कॉल और उनके कॉरस्पोन्डिंग लेनदेन और लिखने के संचालन से बहुत अधिक ओवरहेड नहीं जोड़ता है? – Jordan

+0

@ जोर्डन - लेनदेन के संबंध में, केवल एक लेनदेन होना चाहिए। आपके पास यहां कई लेन-देन नहीं होनी चाहिए। ओवरहेड के बारे में, हाँ, कुछ होंगे। लेकिन, क्या यह देखने के लिए कि क्या प्रत्येक उपयोगकर्ता पहले मौजूद है, 50 (या जो भी) अलग-अलग कॉल करने से बड़ा होगा? इसका पता लगाने का सबसे अच्छा तरीका यह जांचना और देखना है। व्यक्तिगत रूप से, मुझे लगता है कि मैं बार-बार डीबी कॉल करने की कोशिश करके कोशिश/कैच का उपयोग करने में इन-मेमोरी ओवरहेड लेना चाहता हूं। लेकिन यह इस बात पर निर्भर करेगा कि आप आमतौर पर कितने उपयोगकर्ता दर्ज करते हैं। –

3

आप एक क्वेरी

foreach(User user in NewUsers.Where(us => !dbcontext.Users.Any(u => u.userId == us.userId))) 
{ 
    dbcontext.Users.Add(user); 
} 
dbcontext.SaveChanges(); 

संपादित साथ मौजूदा उपयोगकर्ताओं को फ़िल्टर कर सकते। मैं पुष्टि कर सकता हूं कि SQL सर्वर प्रोफाइलर के साथ। प्रोफाइलिंग की

एक intresting परिणाम कुछ हद तक अजीब प्रत्येक आइटम के लिए एफई द्वारा उत्पन्न एसक्यूएल है (मॉडल नाम ओ पी से अलग होते हैं, लेकिन क्वेरी एक ही है):

exec sp_executesql N'SELECT 
CASE WHEN (EXISTS (SELECT 
    1 AS [C1] 
    FROM [dbo].[EventGroup] AS [Extent1] 
    WHERE [Extent1].[EventGroupID] = @p__linq__0 
)) THEN cast(1 as bit) WHEN (NOT EXISTS (SELECT 
    1 AS [C1] 
    FROM [dbo].[EventGroup] AS [Extent2] 
    WHERE [Extent2].[EventGroupID] = @p__linq__0 
)) THEN cast(0 as bit) END AS [C1] 
FROM (SELECT 1 AS X) AS [SingleRowTable1]',N'@p__linq__0 int',@p__linq__0=10 

काफी एक अच्छा टुकड़ा एक साधारण एक लाइनर के काम करने के लिए कोड का।

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

+0

पूरी तरह से काम करता है, और केवल एक बार चलाने की आवश्यकता है (जहां क्वेरी पूरी तालिका पर केवल एक बार चलती है, फिर परिणाम के माध्यम से पुनरावृत्त होती है)। मुझें यह पसंद है। – Jordan

+0

@ p.s.w.g अच्छा पकड़, मैंने देखा कि वाक्यविन्यास त्रुटियों को ठीक करने के लिए मैंने जवाब संपादित किया। – Hari

+0

@ जोर्डन नहीं, ऐसा नहीं है कि 'न्यू यूज़र' एक स्मृति संग्रह है। उस स्थिति में इसे प्रत्येक आइटम के माध्यम से लूप करना होगा और प्रत्येक पर 'Any' का मूल्यांकन करना होगा। यह वास्तव में आपके मूल कोड पर सुधार नहीं करता है, यह इसे अधिक सौंदर्यपूर्ण रूप से सुखद तरीके से पुनर्व्यवस्थित करता है। –

0

निम्न एक्सटेंशन विधि डुप्लिकेट को अनदेखा करते हुए आप किसी भी प्रकार के रिकॉर्ड डालने के लिए अनुमति देगा:

public static void AddRangeIgnore(this DbSet dbSet, IEnumerable<object> entities) 
    { 
     var entitiesList = entities.ToList(); 
     var firstEntity = entitiesList.FirstOrDefault(); 

     if (firstEntity == null || !firstEntity.HasKey() || firstEntity.HasIdentityKey()) 
     { 
      dbSet.AddRange(entitiesList); 
      return; 
     } 

     var uniqueEntities = new List<object>(); 

     using (var dbContext = _dataService.CreateDbContext()) 
     { 
      var uniqueDbSet = dbContext.Set(entitiesList.First().GetType()); 

      foreach (object entity in entitiesList) 
      { 
       var keyValues = entity.GetKeyValues(); 
       var existingEntity = uniqueDbSet.Find(keyValues); 

       if (existingEntity == null) 
       { 
        uniqueEntities.Add(entity); 
        uniqueDbSet.Attach(entity); 
       } 
      } 
     } 

     dbSet.AddRange(uniqueEntities); 
    } 

    public static object[] GetKeyValues(this object entity) 
    { 
     using (var dbContext = _dataService.CreateDbContext()) 
     { 
      var entityType = entity.GetType(); 
      dbContext.Set(entityType).Attach(entity); 
      var objectStateEntry = ((IObjectContextAdapter)dbContext).ObjectContext.ObjectStateManager.GetObjectStateEntry(entity); 
      var value = objectStateEntry.EntityKey 
             .EntityKeyValues 
             .Select(kv => kv.Value) 
             .ToArray(); 
      return value; 
     } 
    } 

    public static bool HasKey(this object entity) 
    { 
     using (var dbContext = _dataService.CreateDbContext()) 
     { 
      var entityType = entity.GetType(); 
      dbContext.Set(entityType).Attach(entity); 
      var objectStateEntry = ((IObjectContextAdapter)dbContext).ObjectContext.ObjectStateManager.GetObjectStateEntry(entity); 
      return objectStateEntry.EntityKey != null; 
     } 
    } 

    public static bool HasIdentityKey(this object entity) 
    { 
     using (var dbContext = _dataService.CreateDbContext()) 
     { 
      var entityType = entity.GetType(); 
      dbContext.Set(entityType).Attach(entity); 
      var objectStateEntry = ((IObjectContextAdapter)dbContext).ObjectContext.ObjectStateManager.GetObjectStateEntry(entity); 
      var keyPropertyName = objectStateEntry.EntityKey 
             .EntityKeyValues 
             .Select(kv => kv.Key) 
             .FirstOrDefault(); 

      if (keyPropertyName == null) 
      { 
       return false; 
      } 

      var keyProperty = entityType.GetProperty(keyPropertyName); 
      var attribute = (DatabaseGeneratedAttribute)Attribute.GetCustomAttribute(keyProperty, typeof(DatabaseGeneratedAttribute)); 
      return attribute != null && attribute.DatabaseGeneratedOption == DatabaseGeneratedOption.Identity; 
     } 
    } 
+0

_डेटा सेवा क्या है? – iivel