इकाई रूपरेखा संस्थाओं में परिवर्तन पूर्ववत करें


116

यह एक तुच्छ प्रश्न हो सकता है लेकिन: चूंकि ADO.NET इकाई ढांचा स्वचालित रूप से परिवर्तनों (उत्पन्न संस्थाओं में) को ट्रैक करता है और इसलिए मूल मान रखता है, मैं इकाई वस्तुओं में किए गए परिवर्तनों को कैसे रोलबैक कर सकता हूं?

मेरे पास एक फॉर्म है जो उपयोगकर्ता को ग्रिड दृश्य में "ग्राहक" संस्थाओं के एक सेट को संपादित करने की अनुमति देता है।

अब मेरे पास दो बटन "एक्सेप्ट" और "रिवर्ट" हैं: यदि "एक्सेप्ट" पर क्लिक किया जाता है, तो मैं कॉल करता हूं Context.SaveChanges()और परिवर्तित ऑब्जेक्ट्स को डेटाबेस में वापस लिखा जाता है। यदि "रिवर्ट" पर क्लिक किया जाता है, तो मैं सभी वस्तुओं के लिए अपने मूल संपत्ति मूल्यों को प्राप्त करना चाहूंगा। उसके लिए कोड क्या होगा?

धन्यवाद

जवाबों:


69

EF में कोई भी परिवर्तन या रद्द करने का कार्य नहीं है। प्रत्येक इकाई में ObjectStateEntryहै ObjectStateManager। राज्य प्रविष्टि में मूल और वास्तविक मूल्य शामिल हैं ताकि आप वर्तमान मूल्यों को अधिलेखित करने के लिए मूल मूल्यों का उपयोग कर सकें लेकिन आपको इसे प्रत्येक इकाई के लिए मैन्युअल रूप से करना होगा। यह नेविगेशन संपत्तियों / संबंधों में बदलावों का सम्मान नहीं करेगा।

"परिवर्तनों को वापस लाने" का सामान्य तरीका संदर्भ और पुनः लोड संस्थाओं को निपटाना है। यदि आप पुनः लोडिंग से बचना चाहते हैं तो आपको संस्थाओं के क्लोन बनाने होंगे और उन क्लोन को नए ऑब्जेक्ट संदर्भ में संशोधित करना होगा। यदि उपयोगकर्ता परिवर्तन रद्द करता है तो आपके पास अभी भी मूल इकाइयाँ होंगी।


4
@LadislavMrnka निश्चित रूप Context.Refresh()से आपके दावे का एक काउंटर-उदाहरण है कि कोई उल्टा ऑपरेशन नहीं है? Refresh()संदर्भ को निपटाने और सभी ट्रैक किए गए परिवर्तनों को खोने से बेहतर दृष्टिकोण (विशिष्ट संस्थाओं पर अधिक आसानी से लक्षित) का उपयोग करना लगता है।
राब

14
@robjb: नहीं। रिफ्रेश केवल एकल इकाई या संस्थाओं के संग्रह को ताज़ा करने में सक्षम है जिसे आप मैन्युअल रूप से परिभाषित करते हैं लेकिन ताज़ा कार्यक्षमता केवल सरल गुणों (संबंधों को नहीं) को प्रभावित करती है। यह भी जोड़ा या हटाए गए संस्थाओं के साथ समस्या का समाधान नहीं करता है।
लादिस्लाव Mrnka

153

गंदे आइटम के लिए DbContext का क्वेरी चेंजट्रैक। हटाए गए आइटम राज्य को अपरिवर्तित और अलग किए गए आइटमों को अलग करने के लिए सेट करें। संशोधित मदों के लिए, मूल मूल्यों का उपयोग करें और प्रविष्टि के वर्तमान मूल्यों को सेट करें। अंत में अपरिवर्तित प्रवेश की संशोधित स्थिति निर्धारित करें:

public void RollBack()
{
    var context = DataContextFactory.GetDataContext();
    var changedEntries = context.ChangeTracker.Entries()
        .Where(x => x.State != EntityState.Unchanged).ToList();

    foreach (var entry in changedEntries)
    {
        switch(entry.State)
        {
            case EntityState.Modified:
                entry.CurrentValues.SetValues(entry.OriginalValues);
                entry.State = EntityState.Unchanged;
                break;
            case EntityState.Added:
                entry.State = EntityState.Detached;
                break;
            case EntityState.Deleted:
                entry.State = EntityState.Unchanged;
                break;
        }
    }
 }

3
धन्यवाद - यह वास्तव में मेरी मदद की!
मैट

5
आपको संभवतः हटाए गए प्रविष्टियों के मूल मूल्यों को भी निर्धारित करना चाहिए। यह संभव है कि आपने पहले एक आइटम को बदल दिया और उसके बाद उसे हटा दिया।
बास डे राड

22
स्थापना Stateके लिए EntityState.Unchanged साथ सभी मूल्यों को पार कर जाएगी Original Valuesऔर साथ ही इसलिए कॉल करने के लिए कोई जरूरत नहीं है SetValuesविधि।
अबॉल्फैज़ल होस्नोडिन

10
इस उत्तर का क्लीनर संस्करण: stackoverflow.com/a/22098063/2498426
जेरथ

1
मेट, यह कमाल है! केवल मैंने जो संशोधन किया है, वह Entries <T> () के जेनेरिक संस्करण का उपयोग करने के लिए है, ताकि यह मेरी रेस्पिरेटरी के लिए काम करे। यह मुझे अधिक नियंत्रण देता है और मैं प्रति इकाई प्रकार वापस रोल कर सकता हूं। धन्यवाद!
डैनियल मैके

33
dbContext.Entry(entity).Reload();

MSDN के लिए आरोही :

डेटाबेस से मान के साथ किसी भी संपत्ति मूल्यों को अधिलेखित करने वाली डेटाबेस से इकाई को पुनः लोड करें। इस पद्धति को कॉल करने के बाद इकाई अपरिवर्तित स्थिति में होगी।

ध्यान दें कि डेटाबेस के लिए अनुरोध के माध्यम से प्राप्त करने में कुछ कमियां हैं:

  • प्रसार यातायात
  • DB अधिभार
  • बढ़ी हुई प्रतिक्रिया का समय

17

यह मेरे लिए काम किया:

dataContext.customer.Context.Refresh(RefreshMode.StoreWins, item);

itemग्राहक इकाई को वापस कहां ले जाना है।


12

किसी भी बदलाव को ट्रैक किए बिना आसान तरीका। यह हर संस्थाओं को देखने से तेज होना चाहिए।

public void Rollback()
{
    dataContext.Dispose();
    dataContext= new MyEntities(yourConnection);
}

एकल पात्रता वस्तु बनाने का समय ... जो कि एमएस (50 एमएस) का एक जोड़ा है। संग्रह के माध्यम से लूपिंग आकार के आधार पर तेज या लंबे समय तक हो सकता है। प्रदर्शन वार ओ (1) शायद ही कभी ओ (एन) की तुलना में एक समस्या है। बिग ओ नोटेशन
गिउश जूल

आपके पीछे नहीं - कनेक्शन को निपटाने और फिर से बनाने का प्रदर्शन। मैंने इसे मौजूदा परियोजना पर परीक्षण किया और यह कुछ हद तक तेजी से ऊपर की Rollbackप्रक्रिया के बाद समाप्त हो गया , जो इसे पूरी तरह से बेहतर विकल्प बनाता है अगर कोई पूरे डेटाबेस राज्य को वापस करना चाहता है। रॉलबैक चेरी ले सकता था।
majkinetor

'n' का अर्थ है वस्तुओं की संख्या। कनेक्शन को फिर से जोड़ने में लगभग 50 ms ... O (1) का अर्थ है कि यह हमेशा एक ही समय होता है 50ms+0*n= 50ms। O (n) का अर्थ है कि प्रदर्शन वस्तुओं की संख्या से प्रभावित होता है ... प्रदर्शन शायद है 2ms+0.5ms*n... इसलिए 96 ऑब्जेक्ट्स को तेज करना लेकिन समय डेटा की मात्रा के साथ रैखिक रूप से बढ़ जाएगा।
गुइश जूल

यदि आप चेरी लेने नहीं जा रहे हैं, तो क्या है (वापस नहीं) यह लुढ़का हुआ तरीका है बशर्ते कि आप बैंडविड्थ के बारे में चिंतित न हों।
एंथोनी निकोल्स

6
// Undo the changes of all entries. 
foreach (DbEntityEntry entry in context.ChangeTracker.Entries()) 
{ 
    switch (entry.State) 
    { 
        // Under the covers, changing the state of an entity from  
        // Modified to Unchanged first sets the values of all  
        // properties to the original values that were read from  
        // the database when it was queried, and then marks the  
        // entity as Unchanged. This will also reject changes to  
        // FK relationships since the original value of the FK  
        // will be restored. 
        case EntityState.Modified: 
            entry.State = EntityState.Unchanged; 
            break; 
        case EntityState.Added: 
            entry.State = EntityState.Detached; 
            break; 
        // If the EntityState is the Deleted, reload the date from the database.   
        case EntityState.Deleted: 
            entry.Reload(); 
            break; 
        default: break; 
    } 
} 

इसने मेरे लिए काम किया। हालाँकि आपको अपने डेटा को पुराने डेटा को लाने के लिए संदर्भ से पुनः लोड करना होगा। स्रोत यहाँ


3

"यह मेरे लिए काम किया:

dataContext.customer.Context.Refresh(RefreshMode.StoreWins, item);

itemग्राहक इकाई को वापस कहां ले जाना है। "


मैंने SQL Azure में ObjectContext.Refresh के साथ परीक्षण किया है, और "RefreshMode.StoreWins" प्रत्येक इकाई के लिए डेटाबेस के खिलाफ एक क्वेरी फायर करता है और एक प्रदर्शन रिसाव का कारण बनता है। Microsoft प्रलेखन के आधार पर ():

ClientWins: ऑब्जेक्ट संदर्भ में ऑब्जेक्ट में किए गए संपत्ति परिवर्तन डेटा स्रोत से मानों से प्रतिस्थापित नहीं किए जाते हैं। SaveChanges के अगले कॉल पर, ये परिवर्तन डेटा स्रोत को भेजे जाते हैं।

StoreWins: ऑब्जेक्ट संदर्भ में ऑब्जेक्ट में किए गए संपत्ति परिवर्तन डेटा स्रोत से मानों के साथ बदल दिए जाते हैं।

ClientWins एक अच्छा विचारधारा नहीं है और न ही, क्योंकि फायरिंग .SaveChanges डेटा स्रोत पर "खारिज" परिवर्तन करेगा।

मुझे नहीं पता कि अभी तक सबसे अच्छा तरीका क्या है, क्योंकि संदर्भ को निपटाना और एक नया निर्माण करना संदेश के साथ एक अपवाद है: "अंतर्निहित प्रदाता खुले में विफल रहा" जब मैं किसी नए संदर्भ पर बनाए गए क्वेरी को चलाने का प्रयास करता हूं।

सादर,

हेनरिक क्लॉसिंग


2

मेरे लिए, बेहतर तरीका यह है EntityState.Unchangedकि आप उस प्रत्येक इकाई पर सेट करें जिस पर आप परिवर्तन करना चाहते हैं। यह आश्वासन परिवर्तन FK पर वापस लौटाए गए हैं और इसमें थोड़ा और अधिक स्पष्ट वाक्यविन्यास है।


4
नोट: यदि इकाई फिर से बदल जाती है तो परिवर्तन वापस आ जाएंगे।
निक व्हले

2

मैंने पाया कि यह मेरे संदर्भ में ठीक काम कर रहा है:

Context.ObjectStateManager.ChangeObjectState(customer, EntityState.Unchanged);


1
मेरा मानना ​​है कि यह कॉल करने पर इकाई के परिवर्तन को रोक देगा DbContext.SaveChanges(), लेकिन यह मूल मूल्यों पर इकाई मूल्यों को वापस नहीं करेगा। और यदि इकाई राज्य बाद के बदलाव से संशोधित हो जाता है, तो संभवतः सभी पिछले संशोधनों को सहेजने पर बरकरार रखा जाएगा?
कार्ल जी

1
इस लिंक कोड की जाँच करें। msdn.microsoft.com/How-to-undo-the-changes-in-00aed3c4 यह कहता है कि Unchaged राज्य में एक इकाई की स्थापना "कवर के तहत" मूल मूल्यों को पुनर्स्थापित करता है।
हनीश

2

यह एक उदाहरण है कि मिर्का किस बारे में बात कर रही है। निम्न विधि मूल मूल्यों के साथ एक इकाई के मौजूदा मूल्यों को ओवरराइट करती है और डेटाबेस को कॉल नहीं करती है। हम DbEntityEntry के OriginalValues ​​संपत्ति का उपयोग करके, और सामान्य तरीके से मान सेट करने के लिए प्रतिबिंब का उपयोग करते हैं। (यह EntityFramework 5.0 के रूप में काम करता है)

/// <summary>
/// Undoes any pending updates 
/// </summary>
public void UndoUpdates( DbContext dbContext )
{
    //Get list of entities that are marked as modified
    List<DbEntityEntry> modifiedEntityList = 
        dbContext.ChangeTracker.Entries().Where(x => x.State == EntityState.Modified).ToList();

    foreach(  DbEntityEntry entity in modifiedEntityList ) 
    {
        DbPropertyValues propertyValues = entity.OriginalValues;
        foreach (String propertyName in propertyValues.PropertyNames)
        {                    
            //Replace current values with original values
            PropertyInfo property = entity.Entity.GetType().GetProperty(propertyName);
            property.SetValue(entity.Entity, propertyValues[propertyName]); 
        }
    }
}

1

हम विरासत वस्तु प्रसंग के साथ EF 4 का उपयोग कर रहे हैं। उपरोक्त समाधानों में से किसी ने भी सीधे मेरे लिए इसका जवाब नहीं दिया - हालांकि यह डीआईडी ​​ने लंबे समय में मुझे सही दिशा में धकेल कर इसका जवाब दिया।

हम सिर्फ संदर्भ नहीं दे सकते हैं और संदर्भ को फिर से बना सकते हैं क्योंकि हमारे पास कुछ ऐसी वस्तुएँ हैं जो स्मृति में लटकी हुई हैं (लानत लोडिंग !! इन मामलों के लिए हमें डेटाबेस को हथौड़ा किए बिना और मौजूदा कनेक्शन को छोड़ने के बिना सब कुछ वापस मूल मूल्यों पर टक्कर देना होगा।

नीचे हमारी इसी समस्या का समाधान है:

    public static void UndoAllChanges(OurEntities ctx)
    {
        foreach (ObjectStateEntry entry in
            ctx.ObjectStateManager.GetObjectStateEntries(~EntityState.Detached))
        {
            if (entry.State != EntityState.Unchanged)
            {
                ctx.Refresh(RefreshMode.StoreWins, entry.Entity);
            }
        }
    }

मुझे उम्मीद है कि इससे दूसरों को मदद मिलेगी।


0

ऊपर कुछ अच्छे विचार, मैंने ICloneable को लागू करने के लिए चुना और फिर एक सरल विस्तार विधि।

यहाँ पाया: मैं सी # में एक सामान्य सूची कैसे क्लोन करूं?

के रूप में इस्तेमाल किया जा करने के लिए:

ReceiptHandler.ApplyDiscountToAllItemsOnReciept(LocalProductsOnReciept.Clone(), selectedDisc);

इस तरह मैं अपनी उत्पाद संस्थाओं की सूची को क्लोन करने में सक्षम था, प्रत्येक आइटम पर छूट लागू करता हूं और मूल इकाई पर किसी भी परिवर्तन को वापस करने के बारे में चिंता करने की ज़रूरत नहीं है। DBContext के साथ बात करने और रिफ्रेशर के साथ रिफ्रेश या काम करने की आवश्यकता नहीं है। आप कह सकते हैं कि मैं EF6 का पूर्ण उपयोग नहीं कर रहा हूं, लेकिन यह बहुत अच्छा और सरल कार्यान्वयन है और DB हिट से बचा जाता है। मैं यह नहीं कह सकता कि इसमें प्रदर्शन हिट है या नहीं।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.