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

Handle view mapping in ExecuteDelete/Update translation #34709

Merged
merged 2 commits into from
Sep 19, 2024

Conversation

roji
Copy link
Member

@roji roji commented Sep 18, 2024

ExecuteDelete/Update take as input the SelectExpression that was translated so far, just prior to their appearance; that SelectExpression is assumed to be reading the tables, so references views rather than tables.

Note that the query may reference multiples tables. The additional tables (not the one being mutated) do need to be views, as we're reading from them. But during translation, we don't know which table is being updated - the user can start from one root, and project out a navigation to be deleted. So we only know the target table at the end of translation.

So the approach here is to detect the case where the table to be changed is a view, find the table corresponding to that view, and replace it in the SelectExpression.

Note that handling ExecuteUpdate where the property to set is a complex type and there's a vide mapping isn't yet supported; this is blocking on #34706.

Closes #34677

@roji roji requested a review from a team September 18, 2024 15:56
// one that refers to the mutable table.

// Get the column on the (mutable) table which corresponds to the property being set
var targetColumnModel = property.DeclaringType.GetTableMappings()
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@AndriySvyryd PTAL, this code finds the mutable table column corresponding to the view column that we got from query translation (so that we can modify the SelectExpression to reference the table instead of the view).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There could be multiple column mappings for the same property, but they should still point to the same column, so you should call FirstOrDefault instead. Or use GroupBy to be safe

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So you're saying there can be multiple column mappings, specifying the same property and same column (basically duplicates)? Does that serve an actual purpose?

Anyway, changed the code to:

var targetColumnModel = property.DeclaringType.GetTableMappings()
    .SelectMany(tm => tm.ColumnMappings)
    .Where(cm => cm.Property == property)
    .Select(cm => cm.Column)
    .SingleOrDefault();

@roji roji force-pushed the UpdateDeleteWithViews branch from d9cbe1c to c9a214f Compare September 18, 2024 16:04
@roji roji force-pushed the UpdateDeleteWithViews branch from c9a214f to f95a56a Compare September 18, 2024 19:10

if (targetColumnModel.Name != column.Name)
{
// If we ever allow mapping the same property to different column names on the view and table, we'll need
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We definitely allow this already

modelBuilder.Entity<Cat>()
    .ToTable("Cat", b => b.Property(c => c.Name).HasColumnName("Name"))
    .ToView("CatView", b => b.Property(c => c.Name).HasColumnName("ViewName"));

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks... Opened #34713 to track this separately, will probably submit a PR for that soon.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note that this affects other columns in the tree, not just the ones in the update setters, which is what this check looks at. Will remove it for now.

/// <summary>
/// Various helpers for query translation.
/// </summary>
public static class QueryHelpers
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️⚠️⚠️ API Review alert ⚠️⚠️⚠️

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep :) This is indeed a thing I wanted to discuss (the naming may need to have EF in it or similar).

Note that the helper method inside - IsMemberAccess - uses extension methods like TryGetEFPropertyArguments, which are completely public (on ExpressionExtension). I think this violates our principle of not exposing public extension methods over non-EF types - users get these in Intellisense when working with extensions. Of course, all this stuff is meant to be provider-facing, we should consider cleaning it up.

@roji roji enabled auto-merge (squash) September 19, 2024 11:16
@roji roji merged commit e84a366 into dotnet:main Sep 19, 2024
7 checks passed
@roji roji deleted the UpdateDeleteWithViews branch September 19, 2024 11:56
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

Successfully merging this pull request may close these issues.

ExecuteUpdate/Delete attempt to update the view, not the table
2 participants