188

Does anyone have suggestions on the most efficient way to implement "update row if it exists else insert" logic using Entity Framework?

Jonathan Wood
  • 59,750
  • 65
  • 229
  • 380

10 Answers10

177

If you are working with attached object (object loaded from the same instance of the context) you can simply use:

if (context.ObjectStateManager.GetObjectStateEntry(myEntity).State == EntityState.Detached)
{
    context.MyEntities.AddObject(myEntity);
}

// Attached object tracks modifications automatically

context.SaveChanges();

If you can use any knowledge about the object's key you can use something like this:

if (myEntity.Id != 0)
{
    context.MyEntities.Attach(myEntity);
    context.ObjectStateManager.ChangeObjectState(myEntity, EntityState.Modified);
}
else
{
    context.MyEntities.AddObject(myEntity);
}

context.SaveChanges();

If you can't decide existance of the object by its Id you must execute lookup query:

var id = myEntity.Id;
if (context.MyEntities.Any(e => e.Id == id))
{
    context.MyEntities.Attach(myEntity);
    context.ObjectStateManager.ChangeObjectState(myEntity, EntityState.Modified);
}
else
{
    context.MyEntities.AddObject(myEntity);
}

context.SaveChanges();
Starnuto di topo
  • 2,192
  • 2
  • 25
  • 48
Ladislav Mrnka
  • 349,807
  • 56
  • 643
  • 654
  • Thanks. Looks like what I need. Can I ask you one question that's been bothering me for a while? Normally, I put my context in a short `using` block. Is it okay to leave the context in memory for a while? For example, during the life of a Windows form? I normally try and clean up database objects to ensure minimum load on the database. Is there no problem waiting to destroy my EF context? – Jonathan Wood Apr 06 '11 at 02:16
  • Check this: http://stackoverflow.com/questions/3653009/entity-framework-and-connection-pooling/3653392#3653392 object context should live as short as possible but in case of winforms or wpf this can mean that context is living as long as presenter. The linked question contains link to msdn article about using nhibernate session in winforms. The same approach can be used for context. – Ladislav Mrnka Apr 06 '11 at 05:34
  • 1
    But what if i need to do this with a list of objects... in my database there is a list of rows with the same id and i want to replace if thew exist or insert if they dont.. how i do it? thanks! – Phoenix_uy Oct 14 '11 at 11:45
  • 1
    This answer LOOKS awesome, but I'm running into this issue on update: An object with the same key already exists in the ObjectStateManager. The ObjectStateManager cannot track multiple objects with the same key. – John Zumbrum Nov 29 '12 at 21:58
  • 1
    Looks like I was just having a bit of an issue with fetching the existing object so as to retrieve its key before doing the update; detaching that lookup object first helped fix it. – John Zumbrum Nov 29 '12 at 22:15
  • @JohnZ so you detached then attached, am I right? Is such a behaviour expensive performance-wise? I am having the same issue. – anar khalilov Jan 15 '14 at 09:56
  • @Anar yes, I did a fetch to get the database version of the object and pull out some of its values, then I detached that version, then attached the object passed into the method, changed its object state and saved. And yeah, it's pretty expensive, because you do a select from the database for each item you are updating (but only in how I do it). – John Zumbrum Jan 15 '14 at 15:49
  • @JohnZ I am having a weird issue where I fetch an existing record, change its field values, then when I call `SaveChanges()` is somehow does not send these changed values to the database. Instead it sends the object with unchanged field values. I have a feeling you already know a solution to this problem. Please take a look, I would very much appreciate it. http://stackoverflow.com/questions/21088398/cannot-update-entity-framework-model – anar khalilov Jan 15 '14 at 15:56
  • Would it not fail if other external processes can modify/add the same record just after you have retrieved dbcontext (in connected state).. – hB0 Jun 24 '15 at 07:39
  • @LadislavMrnka thanks mate - how would the above change now that we have changetracker in ef6? – BKSpurgeon Dec 14 '16 at 05:05
  • hi thank you - another question for the last example given: will it recognise changes in child objects and update those objects? – BKSpurgeon Dec 20 '16 at 05:01
  • @BKSpurgeon: No. You need to detect changes for child objects as well when working with detached entity tree. – Ladislav Mrnka Dec 24 '16 at 08:45
  • If you are using EF6 you can use this line to include an existing entity and update its status to modified: context.Entry(existingBlog).State = EntityState.Modified; – Saad Khan Apr 25 '20 at 20:50
33

As of Entity Framework 4.3, there is an AddOrUpdate method at namespace System.Data.Entity.Migrations:

public static void AddOrUpdate<TEntity>(
    this IDbSet<TEntity> set,
    params TEntity[] entities
)
where TEntity : class

which by the doc:

Adds or updates entities by key when SaveChanges is called. Equivalent to an "upsert" operation from database terminology. This method can be useful when seeding data using Migrations.


To answer the comment by @Smashing1978, I will paste relevant parts from link provided by @Colin

The job of AddOrUpdate is to ensure that you don’t create duplicates when you seed data during development.

First, it will execute a query in your database looking for a record where whatever you supplied as a key (first parameter) matches the mapped column value (or values) supplied in the AddOrUpdate. So this is a little loosey-goosey for matching but perfectly fine for seeding design time data.

More importantly, if a match is found then the update will update all and null out any that weren’t in your AddOrUpdate.

That said, I have a situation where I am pulling data from an external service and inserting or updating existing values by primary key (and my local data for consumers is read-only) - been using AddOrUpdate in production for more than 6 months now and so far no problems.

Community
  • 1
  • 1
Erki M.
  • 4,744
  • 1
  • 42
  • 67
  • 7
    The System.Data.Entity.Migrations namespace contains classes related to code-based migrations and their configurations. Is there any reason why we shouldn't be using this in our repositories for non-migration entity AddOrUpdates? – Matt Lengenfelder Feb 24 '15 at 20:58
  • 11
    Take care with the AddOrUpdate method: http://thedatafarm.com/data-access/take-care-with-ef-4-3-addorupdate-method/ – Colin May 14 '15 at 08:43
  • 1
    This article describe why AddOrUpdate should not be used https://www.michaelgmccarthy.com/2016/08/24/entity-framework-addorupdate-is-a-destructive-operation/ – Nolmë Informatique Oct 15 '19 at 06:49
14

The magic happens when calling SaveChanges() and depends on the current EntityState. If the entity has an EntityState.Added, it will be added to the database, if it has an EntityState.Modified, it will be updated in the database. So you can implement an InsertOrUpdate() method as follows:

public void InsertOrUpdate(Blog blog) 
{ 
    using (var context = new BloggingContext()) 
    { 
        context.Entry(blog).State = blog.BlogId == 0 ? 
                                   EntityState.Added : 
                                   EntityState.Modified; 

        context.SaveChanges(); 
    } 
}

More about EntityState

If you can't check on Id = 0 to determine if it's a new entity or not, check the answer of Ladislav Mrnka.

Community
  • 1
  • 1
Stacked
  • 5,676
  • 5
  • 52
  • 69
10

If you know that you're using the same context and not detaching any entities, you can make a generic version like this:

public void InsertOrUpdate<T>(T entity, DbContext db) where T : class
{
    if (db.Entry(entity).State == EntityState.Detached)
        db.Set<T>().Add(entity);

    // If an immediate save is needed, can be slow though
    // if iterating through many entities:
    db.SaveChanges(); 
}

db can of course be a class field, or the method can be made static and an extension, but this is the basics.

ciscoheat
  • 3,412
  • 1
  • 28
  • 47
4

Ladislav's answer was close but I had to made a couple of modifications to get this to work in EF6 (database-first). I extended my data context with my on AddOrUpdate method and so far this appears to be working well with detached objects:

using System.Data.Entity;

[....]

public partial class MyDBEntities {

  public void AddOrUpdate(MyDBEntities ctx, DbSet set, Object obj, long ID) {
      if (ID != 0) {
          set.Attach(obj);
          ctx.Entry(obj).State = EntityState.Modified;
      }
      else {
          set.Add(obj);
      }
  }
[....]
cdonner
  • 34,608
  • 21
  • 96
  • 146
  • AddOrUpdate also exists as an extension method in System.Data.Entity.Migrations, so if I were you i would avoid to reuse the same method name for your own method. – AFract Jul 24 '18 at 15:43
3

Check existing row with Any.

    public static void insertOrUpdateCustomer(Customer customer)
    {
        using (var db = getDb())
        {

            db.Entry(customer).State = !db.Customer.Any(f => f.CustomerId == customer.CustomerId) ? EntityState.Added : EntityState.Modified;
            db.SaveChanges();

        }

    }
Ali Osman Yavuz
  • 361
  • 2
  • 4
2

In my opinion it is worth to say that with the newly released EntityGraphOperations for Entity Framework Code First you can save yourself from writing some repetitive codes for defining the states of all entities in the graph. I am the author of this product. And I have published it in the github, code-project (includes a step-by-step demonstration and a sample project is ready for downloading) and nuget.

It will automatically set the state of the entities to Added or Modified. And you will manually choose which entities must be deleted if it is not exist anymore.

The example:

Let’s say I have get a Person object. Person could has many phones, a Document and could has a spouse.

public class Person
{
     public int Id { get; set; }
     public string FirstName { get; set; }
     public string LastName { get; set; }
     public string MiddleName { get; set; }
     public int Age { get; set; }
     public int DocumentId {get; set;}

     public virtual ICollection<Phone> Phones { get; set; }
     public virtual Document Document { get; set; }
     public virtual PersonSpouse PersonSpouse { get; set; }
}

I want to determine the state of all entities which is included in the graph.

context.InsertOrUpdateGraph(person)
       .After(entity =>
       {
            // Delete missing phones.
            entity.HasCollection(p => p.Phones)
               .DeleteMissingEntities();

            // Delete if spouse is not exist anymore.
            entity.HasNavigationalProperty(m => m.PersonSpouse)
                  .DeleteIfNull();
       });

Also as you know unique key properties could play role while defining the state of Phone entity. For such special purposes we have ExtendedEntityTypeConfiguration<> class, which inherits from EntityTypeConfiguration<>. If we want to use such special configurations then we must inherit our mapping classes from ExtendedEntityTypeConfiguration<>, rather than EntityTypeConfiguration<>. For example:

public class PhoneMap: ExtendedEntityTypeConfiguration<Phone>
    {
        public PhoneMap()
        {
             // Primary Key
             this.HasKey(m => m.Id);
              …
             // Unique keys
             this.HasUniqueKey(m => new { m.Prefix, m.Digits });
        }
    }

That’s all.

Farhad Jabiyev
  • 23,650
  • 6
  • 59
  • 96
2

Insert else update both

public void InsertUpdateData()
{
//Here TestEntities is the class which is given from "Save entity connection setting in web.config"
TestEntities context = new TestEntities();

var query = from data in context.Employee
            orderby data.name
            select data;

foreach (Employee details in query)
{
    if (details.id == 1)
    {
        //Assign the new values to name whose id is 1
        details.name = "Sanjay";
        details. Surname="Desai";
        details.address=" Desiwadi";
    }
    else if(query==null)
    {
        details.name="Sharad";
        details.surname=" Chougale ";
        details.address=" Gargoti";
    }
}

//Save the changes back to database.
context.SaveChanges();
}
Trikaldarshiii
  • 10,566
  • 16
  • 61
  • 90
1

Alternative for @LadislavMrnka answer. This if for Entity Framework 6.2.0.

If you have a specific DbSet and an item that needs to be either updated or created:

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();

However this can also be used for a generic DbSet with a single primary key or a composite primary key.

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);
    }
}
Ogglas
  • 38,157
  • 20
  • 203
  • 266
-1

Corrected

public static void InsertOrUpdateRange<T, T2>(this T entity, List<T2> updateEntity) 
        where T : class
        where T2 : class
        {
            foreach(var e in updateEntity)
            {
                context.Set<T2>().InsertOrUpdate(e);
            }
        }


        public static void InsertOrUpdate<T, T2>(this T entity, T2 updateEntity) 
        where T : class
        where T2 : class
        {
            if (context.Entry(updateEntity).State == EntityState.Detached)
            {
                if (context.Set<T2>().Any(t => t == updateEntity))
                {
                   context.Set<T2>().Update(updateEntity); 
                }
                else
                {
                    context.Set<T2>().Add(updateEntity);
                }

            }
            context.SaveChanges();
        }
Suraj Rao
  • 28,186
  • 10
  • 88
  • 94
  • 2
    Please use [edit] instead of posting another answer – Suraj Rao Sep 05 '19 at 07:41
  • Sorry to say, but this code is a mess. `T` isn't used, the second `InsertOrUpdate` must be an extension method on `DbSet`, and `context.Set().Any(t => t == updateEntity)` will give a runtime error. Please *test* code before posting! – Gert Arnold Mar 09 '21 at 15:38