एंटिटी फ्रेमवर्क 6 का उपयोग करके रिकॉर्ड कैसे अपडेट करें?


245

मैं EF6 का उपयोग करके रिकॉर्ड को अपडेट करने की कोशिश कर रहा हूं। पहले रिकॉर्ड ढूंढना, यदि मौजूद है, तो उसे अपडेट करें। यहाँ मेरा कोड है: -

var book = new Model.Book
{
    BookNumber =  _book.BookNumber,
    BookName = _book.BookName,
    BookTitle = _book.BookTitle,
};
using (var db = new MyContextDB())
{
    var result = db.Books.SingleOrDefault(b => b.BookNumber == bookNumber);
    if (result != null)
    {
        try
        {
            db.Books.Attach(book);
            db.Entry(book).State = EntityState.Modified;
            db.SaveChanges();
        }
        catch (Exception ex)
        {
            throw;
        }
    }
}

हर बार जब मैं उपरोक्त कोड का उपयोग करके रिकॉर्ड को अपडेट करने की कोशिश करता हूं, तो मुझे यह त्रुटि मिल रही है: -

{System.Data.Entity.Infrastructure.DbUpdateConcurrencyException: स्टोर अपडेट, इंसर्ट या डिलीट स्टेटमेंट ने अप्रत्याशित संख्या में पंक्तियों को प्रभावित किया (0)। संस्थाओं को लोड किए जाने के बाद से एंटिटीज़ को संशोधित या नष्ट किया जा सकता है। ताज़ा करें ObjectStateManager को वापस आना


7
साइड नोट: catch (Exception ex){throw;}बेमानी है और आप इसे पूरी तरह से हटा सकते हैं।
श्रीराम सक्थिवेल

असफलता के कारण का पता लगाने के लिए ब्लॉक पकड़ने की कोशिश करें। लेकिन फिर भी यह नहीं मिला कि यह कोड क्यों विफल हो रहा है?
user1327064

2
इस विषय के विशेषज्ञ नहीं हैं, मैं इस सवाल का जवाब नहीं दे सकता। लेकिन बिना कैच के भी आप ब्रेक का उपयोग कर सकते हैं जब अपवाद होने पर डिबगर को तोड़ने के लिए अपवाद को फेंक दिया जाता है।
श्रीराम सकविवेल १

1
आपने कुछ भी नहीं बदला है। इकाई स्थिति के साथ खेलने से इस तथ्य को नहीं बदला जाएगा कि वस्तु को वास्तव में संशोधित नहीं किया गया है।
जोनाथन एलन

1
खैर, मैंने भी आपके जैसा ही किया और त्रुटि नहीं मिली। अपवाद DbUpdateConcurrencyException कहता है। आपने कंसीडर को कैसे हैंडल किया? क्या आपने टाइमस्टैम्प का उपयोग किया, क्या आपने क्लोन किया और फिर वस्तुओं को फिर से मर्ज किया या आपने सेल्फ-ट्रैकिंग संस्थाओं का उपयोग किया? (3 सबसे अधिक इस्तेमाल किया दृष्टिकोण)। यदि आपने कंसीडर को हैंडल नहीं किया है, तो मुझे लगता है कि यह समस्या है।
एल मैक

जवाबों:


345

आप रिकॉर्ड को अपडेट करने की कोशिश कर रहे हैं (जो मेरे लिए इसका मतलब है "मौजूदा रिकॉर्ड पर एक मूल्य बदलें और इसे वापस सहेजें")। इसलिए आपको ऑब्जेक्ट को पुनः प्राप्त करने, परिवर्तन करने और इसे सहेजने की आवश्यकता है।

using (var db = new MyContextDB())
{
    var result = db.Books.SingleOrDefault(b => b.BookNumber == bookNumber);
    if (result != null)
    {
        result.SomeValue = "Some new value";
        db.SaveChanges();
    }
}

16
मान असाइन करना डेटाबेस को अपडेट नहीं करता है, db.SaveChanges()संदर्भ में संशोधित ऑब्जेक्ट के साथ कॉल करना डेटाबेस को अपडेट करता है।
क्रेग डब्ल्यू।

6
फिर भी यह मुझे मोहित करता है ... इसलिए var परिणाम, वास्तव में dbcontext से जुड़ा हुआ हो जाता है ... तो इसका मतलब यह है कि किसी भी dbcontext सदस्यों द्वारा त्वरित किया गया कोई भी चर वास्तव में डेटाबेस के लिए उस सहयोगी होगा ताकि जो भी परिवर्तन उस चर पर लागू हो , यह भी लागू किया जाता है या कायम रहता है?
वांट इट

6
क्योंकि संदर्भ वस्तु को उत्पन्न करता है संदर्भ वस्तु को ट्रैक कर सकता है, जिसमें वस्तु में परिवर्तन भी शामिल है। जब आप कॉल SaveChangesकरते हैं तो संदर्भ उन सभी ऑब्जेक्ट्स का मूल्यांकन करता है जो यह निर्धारित करने के लिए ट्रैकिंग कर रहे हैं कि क्या उन्हें जोड़ा, बदला, या हटा दिया गया है और कनेक्टेड डेटाबेस के लिए उपयुक्त एसक्यूएल जारी करता है।
क्रेग डब्ल्यू।

3
iam एक ही मुद्दे का सामना कर रहा है - EF6 का उपयोग करके, एक इकाई को अपडेट करने की कोशिश कर रहा है। संलग्न + EntityState.odified काम नहीं कर रहा है। केवल काम करने की चीज है - आपको ऑब्जेक्ट को पुनः प्राप्त करने, वांछित परिवर्तन करने और इसे db.SaveChanges () के माध्यम से सहेजने की आवश्यकता है;
गुरप्रीत सिंह

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

166

मैं एंटिटी फ्रेमवर्क के स्रोत कोड की समीक्षा कर रहा हूं और अगर आपको मुख्य संपत्ति पता है तो वास्तव में एक इकाई को अपडेट करने का एक तरीका मिल गया है:

public void Update<T>(T item) where T: Entity
{
    // assume Entity base class have an Id property for all items
    var entity = _collection.Find(item.Id);
    if (entity == null)
    {
        return;
    }

    _context.Entry(entity).CurrentValues.SetValues(item);
}

अन्यथा, विचारों के लिए AddOrUpdate कार्यान्वयन की जाँच करें ।

उममीद है कि इससे मदद मिलेगी!


12
अच्छा! सभी गुणों की गणना करने की आवश्यकता नहीं है। मुझे लगता है कि मान SaveChanges()सेट करने के बाद कॉल की आवश्यकता है।
जन ज़हरादिक

3
हां, SaveChanges () पर परिवर्तन जारी रहेगा
मिगुएल

1
महान जवाब, IntelliSense के साथ यह भी स्पष्ट नहीं था कि ऐसा कुछ करने से काम नहीं चलेगा: _context.MyObj = newObj; तब SaveChanges () या .... _context.MyObj.Update (newObj) फिर SaveChanges (); सभी गुणों के माध्यम से लूप लिए बिना आपका समाधान संपूर्ण ऑब्जेक्ट को अपडेट करता है।
एडम

7
इससे मुझे शिकायत है कि मैं
वासिली हॉल

3
@VasilyHall - यह तब होता है जब आईडी फ़ील्ड (या जो भी आपने प्राथमिक कुंजी को परिभाषित किया है) मॉडल के बीच भिन्न होते हैं (मॉडल में से एक में नल / 0 सहित)। सुनिश्चित करें कि आईडी दो मॉडल के बीच मेल खाता है और यह ठीक-ठीक अपडेट करेगा।
गाविन 13

51

आप AddOrUpdateविधि का उपयोग कर सकते हैं :

db.Books.AddOrUpdate(book); //requires using System.Data.Entity.Migrations;
db.SaveChanges();

1
आईएमओ सबसे अच्छा समाधान
नॉरगुल

112
.AddOrUpdate()डेटाबेस माइग्रेशन के दौरान उपयोग किया जाता है, यह माइग्रेशन के बाहर इस पद्धति का उपयोग करने के लिए अत्यधिक हतोत्साहित किया जाता है, इसलिए यह Entity.Migrationsनामस्थान में क्यों है ।
एडम विंसेंट

1
जैसा कि @AdamVincent ने कहा, AddOrUpdate()पद्धति माइग्रेशन के लिए अभिप्रेत है और यह उस स्थिति के लिए उपयुक्त नहीं है जब आपको केवल मौजूदा पंक्ति को अपडेट करने की आवश्यकता हो। मामले में जब आपके पास खोज संदर्भ (यानी आईडी) के साथ पुस्तक नहीं है, तो यह नई पंक्ति बनाएगा और यह आने वाले मामलों में एक समस्या हो सकती है (उदाहरण के लिए, आपके पास एक एपीआई है जिसे आपको 404-NotFound प्रतिक्रिया वापस करने की आवश्यकता है यदि आप गैर-मौजूदा पंक्ति के लिए PUT विधि को कॉल करने का प्रयास करें)।
मार्को

4
जब तक आप यह नहीं जानते कि आप क्या कर रहे हैं, इसका उपयोग न करें !!!!!!!!!!!!!!!!!! पढ़ें: michaelgmccarthy.com/2016/08/24/…
Yusha

4
मैं आज फिर से इस पर वापस आया, क्या मैं आप सभी को चेतावनी दे सकता हूं कि यह वांछित उपयोग के मामले के लिए एक अच्छा समाधान नहीं है
Yusha

23

तो आपके पास एक इकाई है जिसे अपडेट किया गया है, और आप इसे कम से कम कोड वाली डेटाबेस में अपडेट करना चाहते हैं ...

कंसीडर हमेशा ट्रिकी होता है, लेकिन मैं यह मान रहा हूं कि आप सिर्फ अपने अपडेट को जीतना चाहते हैं। यहाँ है कि मैंने इसे अपने ही मामले के लिए कैसे किया और अपनी कक्षाओं की नकल करने के लिए नामों को संशोधित किया। दूसरे शब्दों में, बस मेरे लिए बदल attachजाता है add, और यह मेरे लिए काम करता है:

public static void SaveBook(Model.Book myBook)
{
    using (var ctx = new BookDBContext())
    {
        ctx.Books.Add(myBook);
        ctx.Entry(myBook).State = System.Data.Entity.EntityState.Modified;
        ctx.SaveChanges();
    }
}

10

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

using(var context = new ...())
{
    var EditedObj = context
        .Obj
        .Where(x => x. ....)
        .First();

    NewObj.Id = EditedObj.Id; //This is important when we first create an object (NewObj), in which the default Id = 0. We can not change an existing key.

    context.Entry(EditedObj).CurrentValues.SetValues(NewObj);

    context.SaveChanges();
}

2
आपको कम से कम प्रश्न का उत्तर देने की कोशिश करनी चाहिए, न कि केवल कोड पोस्ट करें
स्टेसीगर्ल

कृपया प्रश्न पूछने वाले की बेहतर मदद करने के लिए कोड स्निपेट छोड़ने के बजाय प्रश्न पर कुछ स्पष्टीकरण करें।
feanor07

9

यह कोड पहले रिकॉर्ड को वापस करने के लिए क्वेरी किए बिना केवल कॉलम के एक सेट को अपडेट करने के लिए एक परीक्षण का परिणाम है। यह पहले Entity Framework 7 कोड का उपयोग करता है।

// This function receives an object type that can be a view model or an anonymous 
// object with the properties you want to change. 
// This is part of a repository for a Contacts object.

public int Update(object entity)
{
    var entityProperties =  entity.GetType().GetProperties();   
    Contacts con = ToType(entity, typeof(Contacts)) as Contacts;

    if (con != null)
    {
        _context.Entry(con).State = EntityState.Modified;
        _context.Contacts.Attach(con);

        foreach (var ep in entityProperties)
        {
            // If the property is named Id, don't add it in the update. 
            // It can be refactored to look in the annotations for a key 
            // or any part named Id.

            if(ep.Name != "Id")
                _context.Entry(con).Property(ep.Name).IsModified = true;
        }
    }

    return _context.SaveChanges();
}

public static object ToType<T>(object obj, T type)
{
    // Create an instance of T type object
    object tmp = Activator.CreateInstance(Type.GetType(type.ToString()));

    // Loop through the properties of the object you want to convert
    foreach (PropertyInfo pi in obj.GetType().GetProperties())
    {
        try
        {
            // Get the value of the property and try to assign it to the property of T type object
            tmp.GetType().GetProperty(pi.Name).SetValue(tmp, pi.GetValue(obj, null), null);
        }
        catch (Exception ex)
        {
            // Logging.Log.Error(ex);
        }
    }
    // Return the T type object:         
    return tmp;
}

यहाँ पूरा कोड है:

public interface IContactRepository
{
    IEnumerable<Contacts> GetAllContats();
    IEnumerable<Contacts> GetAllContactsWithAddress();
    int Update(object c);
}

public class ContactRepository : IContactRepository
{
    private ContactContext _context;

    public ContactRepository(ContactContext context)
    {
        _context = context;
    }

    public IEnumerable<Contacts> GetAllContats()
    {
        return _context.Contacts.OrderBy(c => c.FirstName).ToList();
    }

    public IEnumerable<Contacts> GetAllContactsWithAddress()
    {
        return _context.Contacts
            .Include(c => c.Address)
            .OrderBy(c => c.FirstName).ToList();
    }   

    //TODO Change properties to lambda expression
    public int Update(object entity)
    {
        var entityProperties = entity.GetType().GetProperties();

        Contacts con = ToType(entity, typeof(Contacts)) as Contacts;

        if (con != null)
        {
            _context.Entry(con).State = EntityState.Modified;
            _context.Contacts.Attach(con);

            foreach (var ep in entityProperties)
            {
                if(ep.Name != "Id")
                    _context.Entry(con).Property(ep.Name).IsModified = true;
            }
        }

        return _context.SaveChanges();
    }

    public static object ToType<T>(object obj, T type)
    {
        // Create an instance of T type object
        object tmp = Activator.CreateInstance(Type.GetType(type.ToString()));

        // Loop through the properties of the object you want to convert
        foreach (PropertyInfo pi in obj.GetType().GetProperties())
        {
            try
            {
                // Get the value of the property and try to assign it to the property of T type object
                tmp.GetType().GetProperty(pi.Name).SetValue(tmp, pi.GetValue(obj, null), null);
            }
            catch (Exception ex)
            {
                // Logging.Log.Error(ex);
            }
        }
        // Return the T type object
        return tmp;
    }
}    

public class Contacts
{
    public int Id { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string Email { get; set; }
    public string Company { get; set; }
    public string Title { get; set; }
    public Addresses Address { get; set; }    
}

public class Addresses
{
    [Key]
    public int Id { get; set; }
    public string AddressType { get; set; }
    public string StreetAddress { get; set; }
    public string City { get; set; }
    public State State { get; set; }
    public string PostalCode { get; set; }  
}

public class ContactContext : DbContext
{
    public DbSet<Addresses> Address { get; set; } 
    public DbSet<Contacts> Contacts { get; set; } 
    public DbSet<State> States { get; set; }

    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        var connString = "Server=YourServer;Database=ContactsDb;Trusted_Connection=True;MultipleActiveResultSets=true;";
        optionsBuilder.UseSqlServer(connString);
        base.OnConfiguring(optionsBuilder);
    }
}


5

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

@Html.HiddenFor(model=>model.FirstID)
@Html.HiddenFor(model=>model.SecondID)
@Html.HiddenFor(model=>model.Second.SecondID)
@Html.HiddenFor(model=>model.Second.ThirdID)
@Html.HiddenFor(model=>model.Second.Third.ThirdID)

C # कोड में,

[HttpPost]
public ActionResult Edit(First first)
{
  if (ModelState.Isvalid)
  {
    if (first.FirstID > 0)
    {
      datacontext.Entry(first).State = EntityState.Modified;
      datacontext.Entry(first.Second).State = EntityState.Modified;
      datacontext.Entry(first.Second.Third).State = EntityState.Modified;
    }
    else
    {
      datacontext.First.Add(first);
    }
    datacontext.SaveChanges();
    Return RedirectToAction("Index");
  }

 return View(first);
}

5

Attachएक इकाई आईएनजी अपने ट्रैकिंग राज्य को निर्धारित करेगी Unchanged। किसी मौजूदा इकाई को अपडेट करने के लिए, आपको केवल ट्रैकिंग स्थिति को सेट करना होगा ModifiedEF6 डॉक्स के अनुसार :

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

var existingBlog = new Blog { BlogId = 1, Name = "ADO.NET Blog" };

using (var context = new BloggingContext())
{
    context.Entry(existingBlog).State = EntityState.Modified;

    // Do some more work...  

    context.SaveChanges();
}

4
using(var myDb = new MyDbEntities())
{

    user user = new user();
    user.username = "me";
    user.email = "me@me.com";

    myDb.Users.Add(user);
    myDb.users.Attach(user);
    myDb.Entry(user).State = EntityState.Modified;//this is for modiying/update existing entry
    myDb.SaveChanges();
}

4

मुझे एक रास्ता मिला जो ठीक काम करता है।

 var Update = context.UpdateTables.Find(id);
        Update.Title = title;

        // Mark as Changed
        context.Entry(Update).State = System.Data.Entity.EntityState.Modified;
        context.SaveChanges();


1

यहाँ मेरी पोस्ट RIA इकाई-अपडेट विधि (Ef6 समय सीमा के लिए) है:

public static void UpdateSegment(ISegment data)
{
    if (data == null) throw new ArgumentNullException("The expected Segment data is not here.");

    var context = GetContext();

    var originalData = context.Segments.SingleOrDefault(i => i.SegmentId == data.SegmentId);
    if (originalData == null) throw new NullReferenceException("The expected original Segment data is not here.");

    FrameworkTypeUtility.SetProperties(data, originalData);

    context.SaveChanges();
}

ध्यान दें कि FrameworkTypeUtility.SetProperties()एक छोटा सा उपयोगिता कार्य है जिसे मैंने NuGet पर AutoMapper से बहुत पहले लिखा था:

public static void SetProperties<TIn, TOut>(TIn input, TOut output, ICollection<string> includedProperties)
    where TIn : class
    where TOut : class
{
    if ((input == null) || (output == null)) return;
    Type inType = input.GetType();
    Type outType = output.GetType();
    foreach (PropertyInfo info in inType.GetProperties())
    {
        PropertyInfo outfo = ((info != null) && info.CanRead)
            ? outType.GetProperty(info.Name, info.PropertyType)
            : null;
        if (outfo != null && outfo.CanWrite
            && (outfo.PropertyType.Equals(info.PropertyType)))
        {
            if ((includedProperties != null) && includedProperties.Contains(info.Name))
                outfo.SetValue(output, info.GetValue(input, null), null);
            else if (includedProperties == null)
                outfo.SetValue(output, info.GetValue(input, null), null);
        }
    }
}

नोट: केवल तभी काम करता है जब आपके गुण आपके मॉडल में आपके व्यूमॉडल ऑब्जेक्ट के समान हों जो इसमें सहेजे जा रहे हैं।
vapcguy

1

रेनात ने कहा, हटाओ: db.Books.Attach(book);

"AsNoTracking" का उपयोग करने के लिए अपनी परिणाम क्वेरी को भी बदलें, क्योंकि यह क्वेरी एंटिटी फ्रेमवर्क की मॉडल स्थिति को बंद कर रही है। यह सोचता है कि "परिणाम" अब ट्रैक करने के लिए पुस्तक है और आप ऐसा नहीं चाहते हैं।

var result = db.Books.AsNoTracking().SingleOrDefault(b => b.BookNumber == bookNumber);

1

कोशिश करो....

UpdateModel (पुस्तक);

var book = new Model.Book
{
    BookNumber =  _book.BookNumber,
    BookName = _book.BookName,
    BookTitle = _book.BookTitle,
};
using (var db = new MyContextDB())
{
    var result = db.Books.SingleOrDefault(b => b.BookNumber == bookNumber);
    if (result != null)
    {
        try
        {
            UpdateModel(book);
            db.Books.Attach(book);
            db.Entry(book).State = EntityState.Modified;
            db.SaveChanges();
        }
        catch (Exception ex)
        {
            throw;
        }
    }
}

1

मुझे पता है कि इसका उत्तर कुछ समय पहले ही दिया जा चुका है, लेकिन मुझे ऐसा करने का तरीका पसंद है। मुझे उम्मीद है कि यह किसी की मदद करेगा।

//attach object (search for row)
TableName tn = _context.TableNames.Attach(new TableName { PK_COLUMN = YOUR_VALUE});
// set new value
tn.COLUMN_NAME_TO_UPDATE = NEW_COLUMN_VALUE;
// set column as modified
_context.Entry<TableName>(tn).Property(tnp => tnp.COLUMN_NAME_TO_UPDATE).IsModified = true;
// save change
_context.SaveChanges();

1

अगर यह एंटिटी फ्रेमवर्क 6.2.0 के लिए है।

यदि आपके पास एक विशिष्ट DbSetऔर एक आइटम है जिसे या तो अद्यतन या निर्मित करने की आवश्यकता है:

var name = getNameFromService();

var current = _dbContext.Names.Find(name.BusinessSystemId, name.NameNo);
if (current == null)
{
    _dbContext.Names.Add(name);
}
else
{
    _dbContext.Entry(current).CurrentValues.SetValues(name);
}
_dbContext.SaveChanges();

हालाँकि यह DbSetएकल प्राथमिक कुंजी या समग्र प्राथमिक कुंजी के साथ सामान्य के लिए भी उपयोग किया जा सकता है ।

var allNames = NameApiService.GetAllNames();
GenericAddOrUpdate(allNames, "BusinessSystemId", "NameNo");

public virtual void GenericAddOrUpdate<T>(IEnumerable<T> values, params string[] keyValues) where T : class
{
    foreach (var value in values)
    {
        try
        {
            var keyList = new List<object>();

            //Get key values from T entity based on keyValues property
            foreach (var keyValue in keyValues)
            {
                var propertyInfo = value.GetType().GetProperty(keyValue);
                var propertyValue = propertyInfo.GetValue(value);
                keyList.Add(propertyValue);
            }

            GenericAddOrUpdateDbSet(keyList, value);
            //Only use this when debugging to catch save exceptions
            //_dbContext.SaveChanges();
        }
        catch
        {
            throw;
        }
    }
    _dbContext.SaveChanges();
}

public virtual void GenericAddOrUpdateDbSet<T>(List<object> keyList, T value) where T : class
{
    //Get a DbSet of T type
    var someDbSet = Set(typeof(T));

    //Check if any value exists with the key values
    var current = someDbSet.Find(keyList.ToArray());
    if (current == null)
    {
        someDbSet.Add(value);
    }
    else
    {
        Entry(current).CurrentValues.SetValues(value);
    }
}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.