Skip to content
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

Updates cause all properties of an entity to be marked as modified #188

Open
christianacca opened this issue May 16, 2023 · 8 comments
Open

Comments

@christianacca
Copy link

When an EntityInfo whose state is modified is processed by EFPersistenceManager.HandleModified all properties in the corresponding EF Core EntityEntry is marked as being modified.

The exact line that causes this is in MarkEntryAndOwnedChildren:

entry.State = state;

I'm not entirely sure this is a bug or by design? It seems unintentional given the comment here:

image

There was an old related issue but @jtraband closed that one but wasn't sure why?

I wonder if this behaviour can be changed such that not all properties are modified? Or maybe a configuration option to allow this behaviour?

christianacca pushed a commit to christianacca/breeze.server.net that referenced this issue May 19, 2023
@steveschmitt
Copy link
Member

I think the reason is that the entityInfo.OriginalValuesMap cannot be trusted to provide all the fields that might be changed. For example, entity changes could happen in a BeforeSaveEntities handler that might not update the OriginalValuesMap.

I wouldn't want to change this default behavior, because it might break some existing Breeze applications, but it might make sense to make it configurable via something in BreezeConfig.

@christianacca
Copy link
Author

That makes sense. Would you be willing to accept a PR for this change?

@steveschmitt
Copy link
Member

Yes please! A PR would be great.

@steveschmitt
Copy link
Member

@christianacca Have you made any progress on this?

@christianacca
Copy link
Author

Nope! started this weekend building out some test cases around owned entities. Realistically, I would be surprised if I'm able to get a working PR in the next 4 weeks.

@iulianb
Copy link

iulianb commented Apr 18, 2024

entity changes could happen in a BeforeSaveEntities handler

We have this scenario and so far I haven't been able to come up with a workaround.

Some of our entities are mapped to database views and they can be edited on the client. When the changes reach the server we have some bits of infrastructure that "convert" the view entities to their table mapped counterparts. Setting the state to unchanged in HandleModified discards the updates done through the converters and there's no step between that and SaveChangesCore that would allow us to reapply the current values.

I'm thinking that "accepting" changes (by manipulating OriginalValues) on the table entity properties and leaving only the entity as modified in the BeforeSaveEntities step would be an acceptable workaround but haven't got to it yet.

LE: seems to be working

var entry = _context.ChangeTracker.Entries().FirstOrDefault(entry => entry.Entity == convertedEntity);

foreach (var property in _context.Model.FindEntityType(convertedEntity.GetType()).GetProperties())
{
    var current = entry.CurrentValues[property];
    var original = entry.OriginalValues[property];

    if (current != original)
    {
        entry.OriginalValues.SetValues(new Dictionary<string, object>()
        {
            { property.Name, current },
        });
    }
}

@steveschmitt
Copy link
Member

@iulianb I've also faced a case where I had to update "view" entities on the client. Ultimately I handled it on the client: when it was time to save, I queried the appropriate table entities corresponding to the changed view entities, updated them, and saved them. This is easier than removing and adding entities in BeforeSaveEntities, but it does have a performance cost because the table entities are being brought to the client.

To handle it in BeforeSaveEntities would require similar logic. You would need to query the table entities, add them to the SaveMap, update their properties from the corresponding view entities, and remove the view entities from the SaveMap.

It reminds me a bit of this StackOverflow answer, but of course this is a different problem.

@iulianb
Copy link

iulianb commented Apr 18, 2024

@steveschmitt

To handle it in BeforeSaveEntities would require similar logic. You would need to query the table entities, add them to the SaveMap, update their properties from the corresponding view entities, and remove the view entities from the SaveMap.

That is exactly what we're doing in the converters, which are basically command handlers executed before the handlers for create/update/delete. We've opted for this approach because in some scenarios the conversion is not 1 to 1 (eg. one view entity needs to be saved to multiple tables) and we wanted to provide developers with some degree of control over how the conversion happens. After conversion the entities go through the create/update/delete flow as there might be other implementation details, depending on the entity.

As stated in the previous reply, the issue is on updated view entities. Their table counterparts are automatically tracked by EF when queried and any property update on them ends up in the state tracker. When the entity state is set to Unchanged (happens in HandleModified) the change tracker discards these updates and returns the entity to its original state. UpdateOriginalValues does set the original values and updates the state but their current value is lost. The bit of code I posted earlier attempts to set the updated values as original in the state tracker. I'm not 100% sure that it's the correct approach but saving an entity whose tracking information has been altered in this manner seems to work correctly (in the sense that the updated value ends up in the database).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants