Elad Benda
Elad Benda

Reputation: 36674

Does EF upsert have to be done manually?

I want to upsert reference members of an existing entity.

Do I have to write specific code for the upsert?

meaning: I have to check if I'm handling an existing reference member or a new one.

Is there any other simple way to do so?

What happens when you do only Save ?

  public void SaveCofiguration(MamConfiguration_V1Ui itemUi)
        {
            var itemEf = mMamConfiguration_V1UiToEfConvertor.ConvertToNewEf(itemUi);

            using (var maMDBEntities = new MaMDBEntities())
            {
                IDal<MamConfiguration_V1> mamConfigurationDal = mDalFactory.GetDal<MamConfiguration_V1>(maMDBEntities);

                mamConfigurationDal.Save(itemEf);
            }
        }

         public MamConfiguration_V1 GetById(object id)
        {           
                id.ThrowIfNull("id");

                int configurationId = Convert.ToInt32(id);

                var result =
                    mMaMDBEntities.MamConfiguration_V1.SingleOrDefault(item => item.ConfigurationId == configurationId);

                return result;

        }

       public MamConfiguration_V1 Save(MamConfiguration_V1 item)
        {

                item.ThrowIfNull("item");

                var itemFromDB = GetById(item.ConfigurationId);

                if (itemFromDB != null)
                {
                    UpdateEfItem(itemFromDB, item);

                   // if (mMaMDBEntities.ObjectStateManager.GetObjectStateEntry(itemFromDB).State == EntityState.Detached)
//                    {
  //                      mMaMDBEntities.MamConfiguration_V1.AddObject(itemFromDB);
    //                }

                    // Attached object tracks modifications automatically
                    mMaMDBEntities.SaveChanges();

                    return item;
                }





       private void UpdateEfItem(MamConfiguration_V1 itemFromDb, MamConfiguration_V1 itemFromUi)
            {
                itemFromDb.UpdatedDate = DateTime.Now;

                itemFromDb.Description = itemFromUi.Description;

                itemFromDb.StatusId = itemFromUi.StatusId;

                itemFromDb.Name = itemFromUi.Name;

                itemFromDb.NumericTraffic = itemFromUi.NumericTraffic;

                itemFromDb.PercentageTraffic = itemFromUi.PercentageTraffic;

                itemFromDb.Type = itemFromUi.NumericTraffic;

                foreach (var item in itemFromDb.MamConfigurationToBrowser_V1.ToList())
                {
                    if (itemFromUi.MamConfigurationToBrowser_V1.All(b => b.BrowserVersionId != item.BrowserVersionId))
                    {
                        mMaMDBEntities.MamConfigurationToBrowser_V1.DeleteObject(item);
                    }
                }

                for (int i = 0; i < itemFromUi.MamConfigurationToBrowser_V1.Count; i++)
                {
                    var element = itemFromUi.MamConfigurationToBrowser_V1.ElementAt(i);
                    var item = itemFromDb.MamConfigurationToBrowser_V1.SingleOrDefault(b => b.BrowserVersionId == element.BrowserVersionId);
                    if (item != null)
                    {
                        // copy properties from element to item
                    }
                    else
                    {
                        element.Browser = mMaMDBEntities.Browsers.Single(browserItem =>
                            browserItem.BrowserID == element.BrowserID);

                        //element.MamConfiguration_V1 = itemFromDb;

                        //have also tried: element.MamConfiguration_V1 = null;

                        //element.MamConfiguration_V1Reference = null;

                        itemFromDb.MamConfigurationToBrowser_V1.Add(element);
                    }
                }
            }

But I would have expecte Save(itemUi) and SaveChanges() to work fine. No?

Upvotes: 44

Views: 59302

Answers (4)

jjxtra
jjxtra

Reputation: 21160

To avoid the overhead of a query and then insert, or throwing exceptions, you can take advantage of the underlying database support for merges or upserts.

This nuget package does the job pretty well: https://www.nuget.org/packages/FlexLabs.EntityFrameworkCore.Upsert/

Github: https://github.com/artiomchi/FlexLabs.Upsert

Example:

DataContext.DailyVisits
    .Upsert(new DailyVisit
    {
        // new entity path
        UserID = userID,
        Date = DateTime.UtcNow.Date,
        Visits = 1,
    })
    // duplicate checking fields
    .On(v => new { v.UserID, v.Date })
    .WhenMatched((old, @new) => new DailyVisit
    {
        // merge / upsert path
        Visits = old.Visits + 1,
    })
    .RunAsync();

The underlying generated sql does a proper upsert. This command runs right away and does not use change tracking, so that is one limitation.

Upvotes: 22

Beej
Beej

Reputation: 834

"optimistic" approach for simple scenarios (demos)... dbContext.Find()'s intellisense help tells us that it either retrieves entity by key if already present in current context, or queries the database to get it... then we know if it exists to either add or update. i'm using EFCore v2.2.0.

  var existing = _context.Find<InventoryItem>(new object[] {item.ProductId});
  if (existing == null) _context.Add(item);
  else existing.Quantity = item.Quantity;
  _context.SaveChanges();

Upvotes: 1

jaybro
jaybro

Reputation: 1573

See 'AddOrUpdate' method of System.Data.Entity.Migrations.
http://msdn.microsoft.com/en-us/library/system.data.entity.migrations.idbsetextensions.addorupdate%28v=vs.103%29.aspx

using System.Data.Entity.Migrations;

public void Save(Person person) {
    var db = new MyDbContext();
    db.People.AddOrUpdate(person);
    db.SaveChanges();
}

Upvotes: 1

Robert Harvey
Robert Harvey

Reputation: 180878

public void InsertOrUpdate(DbContext context, UEntity entity)
{
    context.Entry(entity).State = entity.Id == 0 ?
                                   EntityState.Added :
                                   EntityState.Modified;
    context.SaveChanges();
}

http://forums.asp.net/t/1889944.aspx/1

Upvotes: 30

Related Questions