-
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
Feature: 2.1: Initial impl. of Query Types. #10647
Conversation
Formerly known a ViewType?? |
src/EFCore/DbView`.cs
Outdated
/// </para> | ||
/// </summary> | ||
/// <typeparam name="TQuery"> The type of view being operated on by this view. </typeparam> | ||
public abstract class DbQuery<TQuery> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
DbQuery [](start = 26, length = 7)
Rename file to match type name
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The rename was pretty heinous 😄
/// </summary> | ||
/// <param name="entityType"> The base type to find types that derive from. </param> | ||
/// <returns> The derived types. </returns> | ||
public static bool IsQueryType([NotNull] this IEntityType entityType) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Copy paste doc error
/// This API supports the Entity Framework Core infrastructure and is not intended to be used | ||
/// directly from your code. This API may change or be removed in future releases. | ||
/// </summary> | ||
public class InternalDbQuery<TQuery> : |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
InternalDbQuery [](start = 17, length = 15)
Rename file
/// and it is not designed to be directly constructed in your application code. | ||
/// </para> | ||
/// </summary> | ||
public class QueryTypeBuilder : IInfrastructure<IMutableModel>, IInfrastructure<InternalEntityTypeBuilder> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Rename file here and generic
var entityType = new EntityType(type, this, ConfigurationSource.Explicit); | ||
|
||
var keyProperty = entityType.AddProperty("__QueryTypeKey__", typeof(int), ConfigurationSource.Convention); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is the reason for adding the fake key here? Does it ever get set to any value, or used in some other way?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This may have been an early requirement to work around the validator. Will try and remove.
@@ -450,7 +450,7 @@ public virtual void Adding_entity_to_local_view_that_is_already_in_the_state_man | |||
[Theory] | |||
[InlineData(false)] | |||
[InlineData(true)] | |||
public virtual void Adding_entity_to_state_manager_of_different_type_than_local_view_type_has_no_effect_on_local_view( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Adding_entity_to_state_manager_of_different_type_than_local_view_type_has_no_effect_on_local_view [](start = 28, length = 97)
Inappropriate rename?
A few questions:
|
@ErikEJ Yes! |
/// This API supports the Entity Framework Core infrastructure and is not intended to be used | ||
/// directly from your code. This API may change or be removed in future releases. | ||
/// </summary> | ||
public virtual EntityType AddQueryType([NotNull] Type type) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can this be added to IMutableModel? I have been working on table value function support based off your vt-new-* branches and need access to this method.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@ajcvickers This would be a break.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is the version after 2.1 a breakable change version? I think some of the other changes for TVF will introduce some breaks as well so it might be better to hold off. I'm also not sure of the current timeline for 2.1, but it seems to be getting close, and I don't want to force something if I can't make that deadline.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure at this stage. I would still try to target 2.1 if you think it's feasible. Include the breaks you would like to make and we can deal with them on a case-by-case basis. - We can often put it workarounds in the short term or suggest alternatives.
1e5f8ae
to
368a0e2
Compare
Yes. Hoping to get a hand from @maumar on this.
Done.
No. That would imply identity. |
I'm planning to test this extensively as soon as I'm done with correlated collections (hopefully one last issue remaining) |
@maumar Thanks...I think. |
@ajcvickers Fake key was removed. This made me think about your question on Owned Types. I'm guessing you were getting at nested objects on Query Types? If so, yeah, feels like we should be able to make it work from the query side. cc @AndriySvyryd for insight here 😄 |
@bricelam Can you take a look at the minor Migrations changes? |
SetAnnotation(name, annotation); | ||
|
||
if (previousLength == _annotations.Value.Count) | ||
if (FindAnnotation(name) != null) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why would you do a double lookup?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What happens if conventions change annotations but _annotations.Count is unchanged?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Conventions use SetAnnotation
instead of AddAnnotation
In reply to: 160510838 [](ancestors = 160510838)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Discussed offline. Problem happens when a convention removes an annotation as a result of a different annotation being added.
@@ -61,7 +61,14 @@ protected virtual void FindSets([NotNull] ModelBuilder modelBuilder, [NotNull] D | |||
{ | |||
foreach (var setInfo in Dependencies.SetFinder.FindSets(context)) | |||
{ | |||
modelBuilder.Entity(setInfo.ClrType); | |||
if (!setInfo.IsQueryType) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Invert condition
var entityTypeWithNullPk | ||
= model.GetEntityTypes() | ||
.Where(et => !et.IsQueryType()) | ||
.FirstOrDefault(et => et.FindPrimaryKey() == null); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Combine conditions
setInfo.Setter.SetClrValue(context, ((IDbSetCache)context).GetOrAddSet(_setSource, setInfo.ClrType)); | ||
setInfo.Setter.SetClrValue( | ||
context, | ||
!setInfo.IsQueryType |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Invert condition
} | ||
|
||
/// <summary> | ||
/// Sets the base type of this view in an inheritance hierarchy. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some leftover references to "view" in this file
(QueryTypeBuilder<TQuery>)base.HasAnnotation(annotation, value); | ||
|
||
/// <summary> | ||
/// Sets the base type of this view in an inheritance hierarchy. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And here too
Check.NotEmpty(relatedTypeName, nameof(relatedTypeName)); | ||
Check.NullButNotEmpty(navigationName, nameof(navigationName)); | ||
|
||
var relatedEntityType = Builder.Metadata.FindInDefinitionPath(relatedTypeName) ?? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will we support weak query types? If not then you can omit the FindInDefinitionPath
call.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will remove.
@@ -55,6 +56,7 @@ public virtual InternalRelationshipBuilder Apply(InternalRelationshipBuilder rel | |||
// Try to invert if one to one or can be converted to one to one | |||
else if ((foreignKey.IsUnique | |||
|| foreignKey.PrincipalToDependent == null) | |||
&& !foreignKey.DeclaringEntityType.IsQueryType() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
foreignKey.DeclaringEntityType.IsQueryType() [](start = 29, length = 44)
Do we support relationships between query types?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not currently.
/// </summary> | ||
/// <param name="entityType"> The entity type. </param> | ||
/// <returns> true if the entity type is a query type; otherwise false. </returns> | ||
public static bool IsQueryType([NotNull] this IEntityType entityType) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should be a property on IEntityType
{ | ||
throw new InvalidOperationException( | ||
CoreStrings.ErrorNavCannotTargetQueryType(targetEntityTypeBuilder.Metadata.DisplayName())); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Move this to ForeignKey.Navigation
[NotNull] Type type, ConfigurationSource configurationSource) | ||
=> Entity(new TypeIdentity(type), configurationSource); | ||
[NotNull] Type type, ConfigurationSource configurationSource, bool throwOnQuery = false) | ||
=> Entity(new TypeIdentity(type), configurationSource, throwOnQuery); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Separate the throwOnQuery = false
overload as something like EntityOrQuery
. You can do this as a separate PR if you want to keep the size down.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@AndriySvyryd - Is this needed? If yes then file an issue.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
entityType.RemoveAnnotation(CoreAnnotationNames.DefiningQuery); | ||
break; | ||
case CoreAnnotationNames.DefiningQuery: | ||
entityType.RemoveAnnotation(RelationalAnnotationNames.TableName); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should only remove annotations added by conventions. Explicit annotations should be removed or trigger an exception in the extension methods themselves.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Removed convention.
out var typeIndexMap).Compile(); | ||
out var typeIndexMap); | ||
|
||
var materializer = materializerExpression.Compile(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why was this separated?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Easier debugging.
@@ -621,7 +622,7 @@ public override void DetectChanges(IStateManager stateManager) | |||
await Assert.ThrowsAsync<ObjectDisposedException>(() => context.FindAsync(typeof(Random), 77)); | |||
|
|||
var methodCount = typeof(DbContext).GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly).Count(); | |||
var expectedMethodCount = 41; | |||
var expectedMethodCount = 42; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Finally we've reached the perfect API size
@@ -134,6 +134,10 @@ public TestModelBuilder HasAnnotation(string annotation, object value) | |||
public abstract TestModelBuilder Entity<TEntity>(Action<TestEntityTypeBuilder<TEntity>> buildAction) | |||
where TEntity : class; | |||
|
|||
public virtual QueryTypeBuilder<TQuery> Query<TQuery>() | |||
where TQuery : class | |||
=> ModelBuilder.Query<TQuery>(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't forget to test the non-generic version
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Migrations changes look good to me
Adds Query Types to the type system. Query Types are like Entity Types but do not require a key to be defined and can only be used for querying. Query Types allow for ad-hoc querying (like anonymous types), but are more flexible because they allow mapping configuration to be specified. E.g. ToTable, HasColumnName etc. Many advanced mapping capabilities are supported, too, such as inheritance mapping and navigations (dependent -> principal only).
@@ -579,7 +590,8 @@ var lastTrackingModifier | |||
.OfType<TrackingResultOperator>() | |||
.LastOrDefault(); | |||
|
|||
return !(queryModel.GetOutputDataInfo() is StreamedScalarValueInfo) | |||
return !_modelExpressionApplyingExpressionVisitor.IsViewTypeQuery |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
left over of ViewType
/// <summary> | ||
/// Relational database specific extension methods for <see cref="QueryTypeBuilder" />. | ||
/// </summary> | ||
public static class RelationalQueryTypeBuilderExtensions |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
File name mismatch
Adds Query Types to the type system. Query Types are like Entity Types but do not require a key to be defined and can only be used for querying. Query Types allow for ad-hoc querying (like anonymous types), but are more flexible because they allow mapping configuration to be specified. E.g. ToTable, HasColumnName etc. Many advanced mapping capabilities are supported, too, such as inheritance mapping and navigations (dependent -> principal only).
#3932, #9290