-
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
Detect unknown shadow primary key values when attempting to save owned collection #19856
Comments
I also ran across this the other week. (Same technical details, except EF Core v3.0.1) To add some hopefully helpful detail, I believe the exact behavior shown is caused by the fact that when It works successfully when the owning entity is loaded I'm not sure what the best general behavior would be for this situation. I can think of only two ways the framework can fulfill the expectation of automatically placing the entity and its owned collection in the supplied state:
Either one seems like it could lead to excessive work at unexpected times. My current workload works well with loading the current values for comparison, so I have a version of |
@keatkeat87 I am able to reproduce this and the analysis from @AngleOSaxon seems correct. @AndriySvyryd Thoughts on this? The generated updates are shown below. We are incorrectly using a temporary value here. It's possible this is a duplicate--I haven't run on master yet. info: Microsoft.EntityFrameworkCore.Database.Command[20101]
Executed DbCommand (1ms) [Parameters=[@p2='1', @p0='22' (Size = 4000), @p1='22' (Size = 4000)], CommandType='Text', CommandTimeout='30']
SET NOCOUNT ON;
UPDATE [Distributors] SET [ShippingCenter_City] = @p0, [ShippingCenter_Street] = @p1
WHERE [Id] = @p2;
SELECT @@ROWCOUNT;
info: Microsoft.EntityFrameworkCore.Database.Command[20101]
Executed DbCommand (1ms) [Parameters=[@p0='-2147482646'], CommandType='Text', CommandTimeout='30']
SET NOCOUNT ON;
DELETE FROM [Distributors_ShippingCenters]
WHERE [Id] = @p0;
SELECT @@ROWCOUNT; |
Every entity need to have the primary key values set to be able to be tracked. For owned reference we propagate the value from the owner, but for owned collection there's no way to get the correct value, so we end up generating and using a temporary value. Perhaps in this case we should throw early if the value generator generates temporary values. @keatkeat87 A workaround is to set the
var principalEntry = context.Entry(distributor);
var i = 0;
foreach (var center in distributor.ShippingCenters)
{
var childEntry = principalEntry.Collection("ShippingCenters").FindEntry(center);
childEntry.Property("Id").CurrentValue = storeIds[i++];
childEntry.State = EntityState.Unchanged;
} |
Notes: this should already throw a better exception in the 5.0 code. However, we may be able to make it better still. It should not mention or use temporary values, since these are a side effect rather than a root cause. |
Thank you for the provided workaround, @AndriySvyryd . But how do you assign the variable In my case the collection is reasonably small. I'd be OK if there were an easy workaround where all previous values are always deleted first, and then the new values are added (even if they are the same). |
…d collection Fixes #19856 The fundamental issue here is that the key for owned collections is, by default, formed from shadow properties. This means the values of these properties are lost when the entities in the collection are no longer tracked by a DbContext. When the entities are then later attached there is no way to get these values back. (Contrast this with owned non-collection entities, where the shadow key value can be synthesized from the owner key.) This in turn means that there is no way to identify these entities in the database, and there is therefore no way to update or delete them. This PR detects this situation and throws an exception with a (hopefully) helpful error message. The best way to deal with this is to add a CLR property for the key (which can be private) to the owned entity type. The key values are then preserved while the entities are not being tracked, and re-attaching the entities works correctly. We should document this.
The fundamental issue here is that the key for owned collections is, by default, formed from shadow properties. This means the values of these properties are lost when the entities in the collection are no longer tracked by a DbContext. When the entities are then later attached there is no way to get these values back. (Contrast this with owned non-collection entities, where the shadow key value can be synthesized from the owner key.) This in turn means that there is no way to identify these entities in the database, and there is therefore no way to update or delete them. The best way to deal with this is to add a CLR property for the key (which can be private) to the owned entity type. The key values are then preserved while the entities are not being tracked, and re-attaching the entities works correctly. We should document this. |
…d collection Fixes #19856 The fundamental issue here is that the key for owned collections is, by default, formed from shadow properties. This means the values of these properties are lost when the entities in the collection are no longer tracked by a DbContext. When the entities are then later attached there is no way to get these values back. (Contrast this with owned non-collection entities, where the shadow key value can be synthesized from the owner key.) This in turn means that there is no way to identify these entities in the database, and there is therefore no way to update or delete them. This PR detects this situation and throws an exception with a (hopefully) helpful error message. The best way to deal with this is to add a CLR property for the key (which can be private) to the owned entity type. The key values are then preserved while the entities are not being tracked, and re-attaching the entities works correctly. We should document this.
…d collection (#25652) * Detect unknown shadow primary key values when attempting to save owned collection Fixes #19856 The fundamental issue here is that the key for owned collections is, by default, formed from shadow properties. This means the values of these properties are lost when the entities in the collection are no longer tracked by a DbContext. When the entities are then later attached there is no way to get these values back. (Contrast this with owned non-collection entities, where the shadow key value can be synthesized from the owner key.) This in turn means that there is no way to identify these entities in the database, and there is therefore no way to update or delete them. This PR detects this situation and throws an exception with a (hopefully) helpful error message. The best way to deal with this is to add a CLR property for the key (which can be private) to the owned entity type. The key values are then preserved while the entities are not being tracked, and re-attaching the entities works correctly. We should document this. * Adding async code and tests. Also, fixes #21206. Let value generators indicate that they return stable values, and don't mark these values as Unknown. This stops values generated for discriminator properties from being marked as Unknown.
When an entity have owned collection, it can't to use Context.Entry and Attach.
when Attach + update the collection, error come out
when Entry + update the collection, ef only generate insert query, doesn't generate delete query,
for owned type is ok, only owned collection have the problem.
Steps to reproduce
Model.cs
Index.cshtml.cs
Further technical details
EF Core version: 3.1.1
Database provider: Microsoft.EntityFrameworkCore.SqlServer
Target framework: .NET Core 3.1.1
Operating system: Windows 10
IDE: Visual Studio 2019 16.4.4
The text was updated successfully, but these errors were encountered: