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

Feature: 2.1: Initial impl. of Query Types. #10647

Merged
merged 2 commits into from
Jan 10, 2018
Merged

Feature: 2.1: Initial impl. of Query Types. #10647

merged 2 commits into from
Jan 10, 2018

Conversation

anpete
Copy link
Contributor

@anpete anpete commented Jan 4, 2018

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

@ErikEJ
Copy link
Contributor

ErikEJ commented Jan 5, 2018

Formerly known a ViewType??

/// </para>
/// </summary>
/// <typeparam name="TQuery"> The type of view being operated on by this view. </typeparam>
public abstract class DbQuery<TQuery>
Copy link
Member

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

Copy link
Contributor Author

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)
Copy link
Member

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> :
Copy link
Member

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>
Copy link
Member

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);

Copy link
Member

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?

Copy link
Contributor Author

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(
Copy link
Member

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?

@ajcvickers
Copy link
Member

A few questions:

  • Does there need to be more test coverage for different types of queries--joins, etc?
  • Can you add a test to WithConstructors... to test creating query types that have parameterized constructors
  • Can query types have owned types?

@anpete
Copy link
Contributor Author

anpete commented Jan 5, 2018

@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)
Copy link
Contributor

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.

Copy link
Contributor Author

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.

Copy link
Contributor

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.

Copy link
Contributor Author

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.

@anpete anpete force-pushed the vt-new-5 branch 2 times, most recently from 1e5f8ae to 368a0e2 Compare January 8, 2018 20:59
@anpete
Copy link
Contributor Author

anpete commented Jan 8, 2018

@ajcvickers

Does there need to be more test coverage for different types of queries--joins, etc?

Yes. Hoping to get a hand from @maumar on this.

Can you add a test to WithConstructors... to test creating query types that have parameterized constructors

Done.

Can query types have owned types?

No. That would imply identity.

@maumar
Copy link
Contributor

maumar commented Jan 8, 2018

I'm planning to test this extensively as soon as I'm done with correlated collections (hopefully one last issue remaining)

@anpete
Copy link
Contributor Author

anpete commented Jan 8, 2018

@maumar Thanks...I think.

@anpete
Copy link
Contributor Author

anpete commented Jan 8, 2018

@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 😄

@anpete
Copy link
Contributor Author

anpete commented Jan 9, 2018

@bricelam Can you take a look at the minor Migrations changes?
@AndriySvyryd Can you take a look at the less minor CF changes?

SetAnnotation(name, annotation);

if (previousLength == _annotations.Value.Count)
if (FindAnnotation(name) != null)
Copy link
Member

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?

Copy link
Contributor Author

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?

Copy link
Member

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)

Copy link
Contributor Author

@anpete anpete Jan 9, 2018

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)
Copy link
Member

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);
Copy link
Member

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
Copy link
Member

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.
Copy link
Member

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.
Copy link
Member

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) ??
Copy link
Member

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.

Copy link
Contributor Author

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()
Copy link
Member

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?

Copy link
Contributor Author

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)
Copy link
Member

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()));
}
Copy link
Member

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);
Copy link
Member

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.

Copy link
Contributor

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.

Copy link
Member

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);
Copy link
Member

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.

Copy link
Contributor Author

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();
Copy link
Member

Choose a reason for hiding this comment

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

Why was this separated?

Copy link
Contributor Author

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;
Copy link
Member

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>();
Copy link
Member

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

Copy link
Contributor

@bricelam bricelam left a 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
Copy link
Contributor

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
Copy link
Contributor

Choose a reason for hiding this comment

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

File name mismatch

@smitpatel
Copy link
Contributor

��U�s�i�n�g� �K�o�r�e�B�u�i�l�d� �2�.�1�.�0�-�p�r�e�v�i�e�w�1�-�1�5�6�5�1�

Why this file? :trollface:


Refers to: temp.txt:1 in 1325b6e. [](commit_id = 1325b6e, deletion_comment = False)

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.

8 participants