EricImhauser
EricImhauser

Reputation: 791

How to clone an entity in Entity Framework Core?

I'm trying to clone an entity using the SetValues method but I get the following error :

The instance of entity type 'TariffPeriod' cannot be tracked because another instance with the same key value for {'Id'} is already being tracked. When attaching existing entities, ensure that only one entity instance with a given key value is attached.

Here is the code :

var period2 = _tariffRepository.GetPeriodFull(period.GUID);
var period3 = new TariffPeriod();
_appDbContext.TariffPeriods.Add(period3);
_appDbContext.Entry(period3).CurrentValues.SetValues(period2);

I see that the error is due to the value of the primary key being copied into the new entity. So, how do I copy the values without the key?

Thanks for your help Eric

Upvotes: 13

Views: 20686

Answers (4)

VladL
VladL

Reputation: 13033

In EF Core 8 I'm just getting the entity with AsNoTracking, setting it's Id to 0 and inserting it again

var myEntity = await _dbContext.MyEntities
    .AsNoTracking()
    .SingleOrDefaultAsync(e => e.EventStart.Date == DateTime.UtcNow.Date);

myEntity.Id = 0;

await _dbContext.MyEntities.AddAsync(myEntity);
await _dbContext.SaveChangesAsync();

Upvotes: 0

Ali Almutawakel
Ali Almutawakel

Reputation: 319

Solution 1

This is my solution based on @grek40's solution with casting added to avoid string literals and allow for future refactoring.

_appDbContext helper method:

    public TEntity DetachedClone<TEntity>(TEntity entity) where TEntity : class
            => Entry(entity).CurrentValues.Clone().ToObject() as TEntity;

For your answer:

    var period2 = _tariffRepository.GetPeriodFull(period.GUID);
    var period3 = _appDbContext.DetachedClone(period2);
    _appDbContext.TariffPeriods.Add(period3);

Solution 2

You can use a simple JSON deep clone function as well. Works like charm. I prefer this method because the first solution involves attaching the entry first using .Entry() and that could be not desirable

    public static T Clone<T>(T source)
    {
        var serialized = JsonConvert.SerializeObject(source);
        return JsonConvert.DeserializeObject<T>(serialized);
    }

(ノ◕ヮ◕)ノ✲゚。⋆

Upvotes: 16

Edward Brey
Edward Brey

Reputation: 41638

Copy the values from the old period to the new period, then set properties with unique values (in this case, the primary key), and lastly add the entity to the DbContext.

var period2 = _tariffRepository.GetPeriodFull(period.GUID);
var period3 = new TariffPeriod();
_appDbContext.Entry(period3).CurrentValues.SetValues(period2);
period3.Id = 0;
_appDbContext.TariffPeriods.Add(period3);

Upvotes: 6

grek40
grek40

Reputation: 13438

You can try getting a clone of the period2 data and modify the Id before assigning to period3

var values = db.Entry(period2).CurrentValues.Clone();
values["Id"] = 0;
db.Entry(period3).CurrentValues.SetValues(values);

Upvotes: 18

Related Questions