69

का उपयोग EF5 के साथ कई वस्तुओं को ट्रैक नहीं कर सकते हैं।ऑब्जेक्टस्टेट प्रबंधक में एक ही कुंजी वाला एक ऑब्जेक्ट पहले से मौजूद है। ObjectStateManager जब डेटाबेस मेरी edmx साथ संग्रहीत procs का उपयोग करने के लिए एक इकाई नवीनीकृत करते समय एक मुद्दे में एक सामान्य भंडार पैटर्न और निर्भरता injenction बनाने और चलाने के लिए Ninject के साथ एक ही कुंजी

DbContextRepository.cs में मेरे अद्यतन है:

public override void Update(T entity) 
{ 
    if (entity == null) 
     throw new ArgumentException("Cannot add a null entity."); 

    var entry = _context.Entry<T>(entity); 

    if (entry.State == EntityState.Detached) 
    { 
     _context.Set<T>().Attach(entity); 
     entry.State = EntityState.Modified; 
    } 
} 

मेरी AddressService.cs से जो मेरे भंडार को वापस चला जाता रहा है:

public int Save(vw_address address) 
{ 
    if (address.address_pk == 0) 
    { 
     _repo.Insert(address); 
    } 
    else 
    { 
     _repo.Update(address); 
    } 

    _repo.SaveChanges(); 

    return address.address_pk; 
} 

यह संलग्न हिट जब और यह EntityState.Modified त्रुटि के साथ pukes:

के साथ एक वस्तु एक ही कुंजी पहले से ही ObjectStateManager में मौजूद है। ऑब्जेक्टस्टेट प्रबंधक एक ही कुंजी के साथ एकाधिक ऑब्जेक्ट्स को ट्रैक नहीं कर सकता है।

मैं ढेर में और इंटरनेट पर और कुछ भी नहीं है कि यह समाधान करता है के साथ आ गए सुझावों में से कई के माध्यम से ध्यान दिया है। आसपास के किसी भी काम की सराहना की जाएगी।

धन्यवाद!

उत्तर

125

संपादित करें: मूल उत्तर Local.SingleOrDefault के बजाय Find का उपयोग किया गया। यह के साथ संयोजन में काम किया @ जुआन Save विधि लेकिन यह डेटाबेस के लिए अनावश्यक प्रश्नों का कारण बन सकता है और else हिस्सा शायद मार डाला कभी नहीं किया गया था (और कुछ भाग को क्रियान्वित करने अपवाद कर लेंगे खोजें पहले से ही डेटाबेस क्वेरी की और इकाई नहीं मिला था तो यह नहीं हो सकता है अद्यतन)। इस मुद्दे को खोजने के लिए @ बेन्सवेन के लिए धन्यवाद।

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

public override void Update(T entity) where T : IEntity { 
    if (entity == null) { 
     throw new ArgumentException("Cannot add a null entity."); 
    } 

    var entry = _context.Entry<T>(entity); 

    if (entry.State == EntityState.Detached) { 
     var set = _context.Set<T>(); 
     T attachedEntity = set.Local.SingleOrDefault(e => e.Id == entity.Id); // You need to have access to key 

     if (attachedEntity != null) { 
      var attachedEntry = _context.Entry(attachedEntity); 
      attachedEntry.CurrentValues.SetValues(entity); 
     } else { 
      entry.State = EntityState.Modified; // This should attach entity 
     } 
    } 
} 

आप देख सकते हैं मुख्य मुद्दा यह है कि SingleOrDefault विधि की जरूरत है इकाई को खोजने के लिए कुंजी जानने के लिए। आप मेरे उदाहरण में कुंजी (IEntity) को उजागर करने वाले सरल इंटरफ़ेस बना सकते हैं और इसे अपनी सभी इकाइयों में कार्यान्वित कर सकते हैं जिन्हें आप इस तरह से संसाधित करना चाहते हैं।

+0

धन्यवाद। तो मैंने int आईडी {get; के साथ एक इंटरफेस आईईएनटीटी बनाया सेट; } तो सार्वजनिक ओवरराइड शून्य अद्यतन (टी इकाई) करने की कोशिश की जहां टी: IEntity लेकिन इसकी नहीं जहां पसंद टी: IEntity। यह भंडार वर्ग यानी सार्वजनिक वर्ग DbContextRepository में है: BaseRepository जहां टी: वर्ग कि अगर एक फर्क नहीं पड़ता। धन्यवाद! – Juan

+1

ऐसे मामले में वर्ग परिभाषा –

+0

हम्म पर सीधे बाधा डाल .. अभी भी बहुत किस्मत नहीं होने चाहिए। मुझे आश्चर्य है अगर इसकी वजह से मैं एक edmx मॉडल का उपयोग कर रहा हूँ। लेकिन मैं वर्ग पर सीधे बाधा डालने के लिए के रूप में यह BaseRepository और IRepository लागू करता असमर्थ हूँ। इसके अलावा एडीएमएक्स में संस्थाएं विचारों से आ रही हैं और प्राथमिक कुंजी एड्रेस_पीके जैसी हैं। – Juan

-2

ऊपर दिया गया उत्तर EF 4.1+ हो सकता है। 4.0 पर उन लोगों के लिए, इस सरल विधि को आजमाएं ... वास्तव में परीक्षण नहीं किया गया लेकिन मेरे परिवर्तनों को संलग्न और सहेज लिया।

public void UpdateRiskInsight(RiskInsight item) 
    { 
     if (item == null) 
     { 
      throw new ArgumentException("Cannot add a null entity."); 
     } 

     if (item.RiskInsightID == Guid.Empty) 
     { 
      _db.RiskInsights.AddObject(item); 
     } 
     else 
     { 
      item.EntityKey = new System.Data.EntityKey("GRC9Entities.RiskInsights", "RiskInsightID", item.RiskInsightID); 
      var entry = _db.GetObjectByKey(item.EntityKey) as RiskInsight; 
      if (entry != null) 
      { 
       _db.ApplyCurrentValues<RiskInsight>("GRC9Entities.RiskInsights", item); 
      } 

     } 

     _db.SaveChanges(); 

    } 
+1

संलग्न होंगे, आप इस तथ्य को पूरी तरह से याद करते हैं कि ओपी एक सामान्य भंडार के साथ ऐसा करना चाहता है। –

7

आप वास्तव में प्रतिबिंब के माध्यम से पहचान नहीं निकाला जा सकता है, नीचे दिए गए उदाहरण देखें:

 var entry = _dbContext.Entry<T>(entity); 

     // Retreive the Id through reflection 
     var pkey = _dbset.Create().GetType().GetProperty("Id").GetValue(entity); 

     if (entry.State == EntityState.Detached) 
     { 
      var set = _dbContext.Set<T>(); 
      T attachedEntity = set.Find(pkey); // access the key 
      if (attachedEntity != null) 
      { 
       var attachedEntry = _dbContext.Entry(attachedEntity); 
       attachedEntry.CurrentValues.SetValues(entity); 
      } 
      else 
      { 
       entry.State = EntityState.Modified; // attach the entity 
      } 
     } 
+0

यह सुनिश्चित नहीं है कि आपको सामान्य तरीके से '_dbset' कैसे मिलेगा ... –

+0

@SerjSagan आप बस' _dbContext.Set () कर सकते हैं।)()। GetTy..' –

2

@serj-sagan आप इस तरह से यह करना चाहिए:

** ध्यान दें कि YourDb एक वर्ग होना चाहिए डीबीकॉन्टेक्स्ट से व्युत्पन्न

public abstract class YourRepoBase<T> where T : class 
{ 
    private YourDb _dbContext; 
    private readonly DbSet<T> _dbset; 

    public virtual void Update(T entity) 
    { 
     var entry = _dbContext.Entry<T>(entity); 

     // Retreive the Id through reflection 
     var pkey = _dbset.Create().GetType().GetProperty("Id").GetValue(entity); 

     if (entry.State == EntityState.Detached) 
     { 
      var set = _dbContext.Set<T>(); 
      T attachedEntity = set.Find(pkey); // access the key 
      if (attachedEntity != null) 
      { 
       var attachedEntry = _dbContext.Entry(attachedEntity); 
       attachedEntry.CurrentValues.SetValues(entity); 
      } 
      else 
      { 
       entry.State = EntityState.Modified; // attach the entity 
      } 
     } 
    } 

} प्रतिबिंब के बिना

0

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

private void Update<T>(T entity, Func<ObservableCollection<T>, T> locatorMap) where T : class 
{ 
    var entry = Context.Entry(entity); 
    if (entry.State == EntityState.Detached) 
    { 
     var set = Context.Set<T>(); 
     T attachedEntity = locatorMap(set.Local); 

     if (attachedEntity != null) 
     { 
      var attachedEntry = Context.Entry(attachedEntity); 
      attachedEntry.CurrentValues.SetValues(entity); 
     } 
     else 
     { 
      entry.State = EntityState.Modified; // This should attach entity 
     } 
    } 
} 

आप इस तरह यह कहेंगे:

Update(EntitytoUpdate, p => p.SingleOrDefault(a => a.Id == id)) 
8

मैं अपने ऑटो अपवित्र नहीं करना चाहता था जोड़ने इंटरफेस, या विशेषताओं द्वारा एफई वर्गों उत्पन्न। तो यह वास्तव में ऊपर जवाब में से कुछ से एक छोटा सा (ताकि क्रेडिट Ladislav Mrnka को जाता है) है। यह मेरे लिए एक आसान समाधान प्रदान किया।

मैं अद्यतन विधि है कि संस्था की पूर्णांक कुंजी पाया करने के लिए एक समारोह जोड़ा।

public void Update(TEntity entity, Func<TEntity, int> getKey) 
{ 
    if (entity == null) { 
     throw new ArgumentException("Cannot add a null entity."); 
    } 

    var entry = _context.Entry<T>(entity); 

    if (entry.State == EntityState.Detached) { 
     var set = _context.Set<T>(); 
     T attachedEntity = set.Find.(getKey(entity)); 

     if (attachedEntity != null) { 
      var attachedEntry = _context.Entry(attachedEntity); 
      attachedEntry.CurrentValues.SetValues(entity); 
     } else { 
      entry.State = EntityState.Modified; // This should attach entity 
     } 
    } 
} 

फिर जब आप अपने कोड कहते हैं, आप उपयोग कर सकते हैं ..

repository.Update(entity, key => key.myId); 
+0

क्या आपको 'सेट का उपयोग नहीं करना चाहिए। 'Set.Find' के बजाय स्थानीय। ढूँढें'? मेरा मानना ​​है कि आपका कोड हमेशा डेटाबेस को हिट करेगा, इस प्रकार कभी 'संलग्न एंटीटी' परिवर्तनीय शून्य नहीं बनायेगा। https://msdn.microsoft.com/en-us/library/jj592872(v=vs.113).aspx –

0

आप AsNoTracking करने के लिए अपने संदर्भ() इस स्मृति में इकाई में परिवर्तन पर नज़र रखने aspmvc बंद हो जाएगा (जो सेट करते हैं आप वेब पर वैसे भी क्या चाहते हैं)।

_dbContext.Products.AsNoTracking().Find(id); 

मैं तुम्हें http://www.asp.net/mvc/tutorials/getting-started-with-ef-using-mvc/advanced-entity-framework-scenarios-for-an-mvc-web-application

1

एक अन्य समाधान पर इस बारे में अधिक पढ़ने की सिफारिश करेंगे (@ सेर्गेई के जवाब के आधार पर) हो सकता है:

private void Update<T>(T entity, Func<T, bool> predicate) where T : class 
{ 
    var entry = Context.Entry(entity); 
    if (entry.State == EntityState.Detached) 
    { 
     var set = Context.Set<T>(); 
     T attachedEntity = set.Local.SingleOrDefault(predicate); 
     if (attachedEntity != null) 
     { 
      var attachedEntry = Context.Entry(attachedEntity); 
      attachedEntry.CurrentValues.SetValues(entity); 
     } 
     else 
     { 
      entry.State = EntityState.Modified; // This should attach entity 
     } 
    } 
} 

और फिर आप इसे इस तरह कहेंगे:

Update(EntitytoUpdate, key => key.Id == id) 
-3

आप ऑब्जेक्ट fBLL = new FornecedorBLL(); ऑब्जेक्ट को निकालने के लिए भूल गए हैं I n algun place

0

मिली इकाई को अलग करना (attachedEntityLadislav's समाधान में देखें) और संशोधित एक को फिर से जोड़ने से मेरे लिए ठीक काम किया गया।

इस के पीछे तर्क सरल है: अगर कुछ अपरिवर्तनीय है तो यह (एक पूरे के रूप, इकाई) जहां यह वांछित एक के साथ संबंध रखता है से बदल दें।

यहां ऐसा करने का एक उदाहरण है:

var set = this.Set<T>(); 
if (this.Entry(entity).State == EntityState.Detached) 
{ 
    var attached = set.Find(id); 
    if (attached != null) { this.Entry(attached).State = EntityState.Detached; } 
    this.Attach(entity); 
} 

set.Update(entity); 
बेशक

, एक आसानी से अंदाजा लगा सकते हैं कि इस स्निपेट एक सामान्य विधि का हिस्सा है, इसलिए T है, जो एक टेम्पलेट पैरामीटर है का उपयोग है, और Set<T>()