22

I want to achieve the ADDorUpdate() method in Generic repository using EF Core like below? Can anyone help me?

  public virtual void AddOrUpdate(T entity)
    {
        #region Argument Validation

        if (entity == null)
        {
            throw new ArgumentNullException("entity");
        }
        #endregion
         DbSet.AddOrUpdate(e => e.Id, entity);  
        this.DbContext.SaveChanges();
    }
Naurto san
  • 277
  • 1
  • 2
  • 14

3 Answers3

50

Simply use

context.Update(entity);

It does exactly AddOrUpdate based on value of entity PrimaryKey (0 means Add, > 0 means Update):

public virtual void AddOrUpdate(T entity)
{
    if (entity == null)
        throw new ArgumentNullException("entity");

     this.DbContext.Update(entity);  
     this.DbContext.SaveChanges();
}
Arman Ebrahimpour
  • 4,252
  • 1
  • 14
  • 46
  • Hi Arman, I have found the same in Microsoft docs. But read an article in the internet as it's not gonna solve. Is it? – Naurto san Jun 18 '20 at 12:07
  • @Naurtosan Behavior of Update in EF Core 3 is what I said and it will work – Arman Ebrahimpour Jun 18 '20 at 12:10
  • Thanks, @Arman. I will check this. AndI have a small confusion, in my class, we have declared dbonctext and dbset . And in my constructor, we are setting like this.DbContext = dbContext; this.DbSet = this.DbContext.Set(); So my question is can I set like Dbset.Update(entity); instead of this.DbContext.Update(entity); . in my method. Can you please give reply ? – Naurto san Jun 18 '20 at 12:31
  • @Naurtosan Yes you can. Both of them do the same thing – Arman Ebrahimpour Jun 18 '20 at 13:06
  • See https://learn.microsoft.com/en-us/ef/core/saving/disconnected-entities#saving-single-entities the primary key issue was not causing this to work for me. – perustaja Dec 26 '20 at 03:12
  • does this actually work for anyone? I'm using EF 3,1, when trying to update a non existent entry I'm getting this exception " Data may have been modified or deleted since entities were loaded." – kewur Apr 12 '21 at 04:50
  • 1
    What about when you aren't tracking entities though and you have a dup key conflict? An efficient raw SQL/MySQL query method is to "ON DUPLICATE KEY UPDATE" when a constraint is violated on a query and then update the instead. Otherwise you have dup queries to query a row to see if it exists then deciding to add or update it in your code, its less efficient and won't scale well where the other way does directly with the database engine you give it the data, it decides how to handle it with one query and less of a performance penalty. And in your code you may not know yet if its a dup... – fr332lanc3 Apr 17 '21 at 22:38
  • 39
    NOTE: This only works for AUTOGENERATED keys, otherwise you will get ConcurrencyException – Poul Bak Jun 05 '21 at 21:53
  • 1
    It throws error: The database operation was expected to affect 1 row(s), but actually affected 0 row(s); data may have been modified or deleted since entities were loaded. See http://go.microsoft.com/fwlink/?LinkId=527962 for information on understanding and handling optimistic concurrency exceptions. – Ali Adravi Oct 24 '22 at 23:35
  • I just checked myself: if (ctx.Orders.Any(o => o.OrderId == order.OrderId)) ctx.Orders.Update(order); else ctx.Orders.Add(order); – Rob Sedgwick May 20 '23 at 10:11
3

If you work with composite keys you can use next method I wrote:

public static void AddOrUpdateRange<TEntity>(this DbSet<TEntity> set, IEnumerable<TEntity> entities)
            where TEntity : class
        {
            foreach (var entity in entities)
            {
                _ = !set.Any(e => e == entity) ? set.Add(entity) : set.Update(entity);
            }
        }
Mounir
  • 155
  • 10
  • This will send request to DB for every entity. Also all calls to DB should be `async` (use `AnyAsync` in your case). – maxc137 Jun 30 '21 at 12:14
  • @МаксимКошевой the Add() or Update() will only change the local data. Only when calling SaveChange() will interact with database. – Koen van der Linden Aug 26 '21 at 09:14
  • 3
    @Koen van der Linden I'm talking about `set.Any`. It will send a request to db for every entity. – maxc137 Aug 26 '21 at 10:35
0

You can check if the entity exists. Like this:

book exists= context.Set<T>().Any(x=>x.Id==entity.Id);

Use like this.

if(exists)
context.Set<T>().Add(entity);
else
context.Set<T>().Update(entity);

Edit: Since this is a generic method, you should create a constraint on the method for this to work. For example, an abstract class.

public abstract class BaseEntity
{
public int Id {get; set; }
}

Your method should then have this constraint.

public virtual void AddOrUpdate(T entity)
 where T: BaseEntity{}

Your Entity should inherit from BaseEntity.

bolkay
  • 1,881
  • 9
  • 20
  • Hi Bolkey, getting an error as "Operator == cannot be applied to operends of type 'Tkey ' and T'key'" – Naurto san Jun 18 '20 at 12:05
  • Thanks for the reply @Bolkay. Please find the link for IGenricRepo and GNericRepo https://www.c-sharpcorner.com/forums/the-name-savechanges-does-not-exist-in-the-current-context – Naurto san Jun 19 '20 at 05:18