Rob
Rob

Reputation: 7217

EF Core update an existing entity

I'm using EF Core and .NET 6 and I would like to essentially upsert an entity to a table - a fairly simple ask.

I have the following code:

var countries = GetCountries();

using (var scope = scopeFactory.CreateScope())
{
    var dbContext = scope.ServiceProvider.GetRequiredService<ApplicationDbContext>();

    foreach (var c in countries)
    {
        // check if the country already exists.
        var exists = dbContext.Countries.Where(d => d.Id == c.Id).FirstOrDefault();

        // if already exists - update (rather than add).
        if (exists != null) 
        {
            exists.Name = c.Name;
            exists.DisplayName = c.DisplayName;
            ... // omitted other prop updates.

            dbContext.Countries.Update(exists);
        } 
        else
        {
            dbContext.Countries.Add(c);
        }
    }

    await dbContext.SaveChangesAsync();
}

I was wondering - is there was a more efficient way to update without manually looking up then updating (it's not very performant).

Preferably, I was hoping there was a Merge method in EF Core but can't find anything useful (that's free...). Rather than doing the manual lookup and update in this way.

I'm probably missing something very obvious here - thanks for any pointers in advance!

Upvotes: 2

Views: 9867

Answers (3)

Stefan B.
Stefan B.

Reputation: 1

The answer by user19087368 seems the most straight forward. I tested it on .NET 6 and it worked perfectly - adapted a little bit to my usecase:

Here is my version of it with the "guid" as the primary key:

public async Task CreateOrUpdateApplication(Application application)
{
    var itemExists = _dbContext
        .Application
        .Any(i => i.ApplicationGuid == application.ApplicationGuid);
    _dbContext.Entry(application).State = itemExists ? 
        EntityState.Modified : EntityState.Added;
    await _dbContext.SaveChangesAsync();
}

Upvotes: 0

Svyatoslav Danyliv
Svyatoslav Danyliv

Reputation: 27282

EF Core do not have Merge, or similar for Upsert. You can improve performance of your query by selecting existng items in one batch. Also you do not need to call Update, just change properties.

var countries = GetCountries();

using (var scope = scopeFactory.CreateScope())
{
    var dbContext = scope.ServiceProvider.GetRequiredService<ApplicationDbContext>();

    var countyIds = countries.Select(c => c.Id);
    var existing = (await dbContext.Countries.Where(d => countyIds.Contains(d.Id))
        .ToListAsync())
        .ToDictionary(c => c.Id);

    foreach (var c in countries)
    {
        // check if the country already exists.

        if (existing.TryGetValue(c.Id, out var toUpdate))
        {
            // if already exists - update (rather than add).
            toUpdate.Name = c.Name;
            toUpdate.DisplayName = c.DisplayName;
            ... // omitted other prop updates.
        } 
        else
        {
            dbContext.Countries.Add(c);
        }
    }

    await dbContext.SaveChangesAsync();
}

Upvotes: 5

user19087368
user19087368

Reputation: 53

public void InsertOrUpdate(Entity entity) 
{ 
    using (var context = new dbContext.Countries()) 
    { 
        context.Entry(entity).State = entity.Id == 0 ? 
                                   EntityState.Added : 
                                   EntityState.Modified; 

        context.SaveChanges(); 
    } 
}

Upvotes: -2

Related Questions