Reputation: 791
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
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
Reputation: 319
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);
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
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
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