-
Notifications
You must be signed in to change notification settings - Fork 3.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
How to update an entity with collection property that has either new, updated or deleted entities? #26830
Comments
@LeonarddeR There is no easy answer to this. EF needs to know whether each entity is new or not, but if the entities come to EF with pre-generated keys, then there is no way for EF to automatically distinguish new entities from existing ones. Further, there is no way for EF to know if an entity has been removed from a collection as opposed to never being there in the first place. This is discussed in Explicitly Tracking Entities in the documentation. In general, the ways to deal with this are:
Also, consider voting to Turn-key Disconnected Entities Solution. |
Thanks for your reply!
This makes sense. I have no problem with querying the database for an existing entity, it's just the question how to update it properly. I assume that:
Gets the current subscription with current lines from the database. Then, it should be relatively easy to find out whether the lines on a new subscription are tracked by EF or not. Is that correct? E.g. does |
@LeonarddeR Typically, this kind of thing is done through the EntityEntry and/or PropertyEntry APIs. For example, if you have a loaded entity, then you can apply current or original values to that entity from another instance or, from an instance of a DTO. |
Thanks for pointing me at these docs, I must have missed them. Code is now as follows: public async Task<Int32> CreateOrUpdateEntitiesAsync(List<TModel> entities, CancellationToken cancellationToken)
{
TId[]? existingIds = await this.GetExistingIdsAsync(entities, cancellationToken);
foreach (TModel? entity in entities)
{
TId? idValue = this.ModelInfo.IdentifierValue<TModel, TId>(entity);
if (existingIds.Contains(idValue))
{
EntityEntry<TModel> entry = this._dbContext.Update(entity);
foreach (var collection in entry.Collections)
{
if (collection.CurrentValue is null)
{
continue;
}
await collection.LoadAsync(cancellationToken);
IEnumerable<EntityEntry> subEntries = collection.CurrentValue.Cast<Object>().Select(e => this._dbContext.Entry(e));
foreach (var subEntry in subEntries)
{
if (subEntry.State == EntityState.Unchanged)
{
// This is a deleted sub entry.
subEntry.State = EntityState.Deleted;
}
else if ((await subEntry.GetDatabaseValuesAsync(cancellationToken)) is null)
{
subEntry.State = EntityState.Added;
}
}
}
}
else
{
this._dbContext.Add(entity);
}
} LoadAsync turned out to be very helpful to filter out the entities to delete. There's one major disadvantage to the current code, namely that I need to call |
@LeonarddeR It's not clear to me exactly what queries that method is executing. If the existing entity is queried with a tracking query, then EF keeps track of the "original values" of that entity, and can therefore determine automatically if any value has changed. There should be no need for |
The code calling the |
@LeonarddeR Is it not possible to query for the graph of entities that might have changed and all their children, rather than trying to lookup each one individually? Once you have the graph loaded with all entities in the |
Would you be able to provide a small code example to get started? A problem I might not have mentioned before is that I know neither the collection properties nor their types when querying the graph. So the base type might either be Subscription with a SubscriptionLines property of type IEnumerable it could also be an Invoice type with the InvoiceLines collection of type IEnumerable. |
@LeonarddeR I have filed More samples for disconnected entities in the docs repo to track creating samples for the common ways to do disconnected entities. |
Thanks for this! |
Closing this; adding sample code tracked by dotnet/EntityFramework.Docs#3601 |
I have the following models:
I'm syncing these models to a database from an external source where a subscription comes in with a bunch of subscription lines. All the Ids are generated by the external source. Therefore:
I'm not sure how to accomplish this, as I read in the documentation that a call to Update sets the state of the Subscription entity and all the SubscriptionLines to Modified. However, this means that entities are being updated that are not yet part of the database, which will fail of course.
When the subscription entity is known to the database, I can of course remove it, which will remove all the subscription lines, and then reinsert it. However, this looks like an overkill strategy. I read that updating a collection i.e. clearing it and than adding the new items does not work.
I'm asking this here because I think this is a use case that makes sense to be documented in the official documentation.
The text was updated successfully, but these errors were encountered: