Skip to content

Commit

Permalink
Add a convention to create the entity type returned by a queryable fu…
Browse files Browse the repository at this point in the history
…nction.

Store mapped EntityType in the DbFunction.
Extract InternalDbFunctionBuilder and InternalDbFunctionParameterBuilder from DbFunctionBuilder and DbFunctionParameterBuilder.
Add more conditions to metadata API consistency test.

Part of #20051
Fixes #20160
  • Loading branch information
AndriySvyryd committed Mar 27, 2020
1 parent 8bd99a9 commit f292936
Show file tree
Hide file tree
Showing 50 changed files with 1,680 additions and 961 deletions.
1 change: 0 additions & 1 deletion src/EFCore.Cosmos/Query/Internal/SelectExpression.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
using System.Linq.Expressions;
using JetBrains.Annotations;
using Microsoft.EntityFrameworkCore.Cosmos.Internal;
using Microsoft.EntityFrameworkCore.Diagnostics;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Query;
using Microsoft.EntityFrameworkCore.Utilities;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -264,37 +264,6 @@ public static bool CanSetSchema(
return entityTypeBuilder.CanSetAnnotation(RelationalAnnotationNames.Schema, schema, fromDataAnnotation);
}

/// <summary>
/// Configures the entity as a result of a queryable function. Prevents a table from being created for this entity.
/// </summary>
/// <param name="entityTypeBuilder"> The builder for the entity type being configured. </param>
/// <returns> The same builder instance so that multiple calls can be chained. </returns>
public static EntityTypeBuilder ToQueryableFunctionResultType(
[NotNull] this EntityTypeBuilder entityTypeBuilder)
{
Check.NotNull(entityTypeBuilder, nameof(entityTypeBuilder));

entityTypeBuilder.Metadata.SetAnnotation(RelationalAnnotationNames.QueryableFunctionResultType, null);

return entityTypeBuilder;
}

/// <summary>
/// Configures the entity as a result of a queryable function. Prevents a table from being created for this entity.
/// </summary>
/// <param name="entityTypeBuilder"> The builder for the entity type being configured. </param>
/// <returns> The same builder instance so that multiple calls can be chained. </returns>
public static EntityTypeBuilder<TEntity> ToQueryableFunctionResultType<TEntity>(
[NotNull] this EntityTypeBuilder<TEntity> entityTypeBuilder)
where TEntity : class
{
Check.NotNull(entityTypeBuilder, nameof(entityTypeBuilder));

entityTypeBuilder.Metadata.SetAnnotation(RelationalAnnotationNames.QueryableFunctionResultType, null);

return entityTypeBuilder;
}

/// <summary>
/// Configures the view that the entity type maps to when targeting a relational database.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -531,11 +531,6 @@ public static bool IsIgnoredByMigrations([NotNull] this IEntityType entityType)
return false;
}

if (entityType.FindAnnotation(RelationalAnnotationNames.QueryableFunctionResultType) != null)
{
return true;
}

var viewDefinition = entityType.FindAnnotation(RelationalAnnotationNames.ViewDefinition);
if (viewDefinition?.Value != null)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,11 @@ public static SequenceBuilder HasSequence(
[NotNull] this ModelBuilder modelBuilder,
[NotNull] string name,
[CanBeNull] string schema = null)
{
Check.NotNull(modelBuilder, nameof(modelBuilder));
Check.NotEmpty(name, nameof(name));
Check.NullButNotEmpty(schema, nameof(schema));

return new SequenceBuilder(GetOrAddSequence(modelBuilder, name, schema));
}
=> new SequenceBuilder(HasSequence(
Check.NotNull(modelBuilder, nameof(modelBuilder)).Model,
name,
schema,
ConfigurationSource.Explicit));

/// <summary>
/// Configures a database sequence when targeting a relational database.
Expand Down Expand Up @@ -65,9 +63,6 @@ public static ModelBuilder HasSequence(
[CanBeNull] string schema,
[NotNull] Action<SequenceBuilder> builderAction)
{
Check.NotNull(modelBuilder, nameof(modelBuilder));
Check.NotEmpty(name, nameof(name));
Check.NullButNotEmpty(schema, nameof(schema));
Check.NotNull(builderAction, nameof(builderAction));

builderAction(HasSequence(modelBuilder, name, schema));
Expand All @@ -89,12 +84,10 @@ public static SequenceBuilder HasSequence(
[NotNull] string name,
[CanBeNull] string schema = null)
{
Check.NotNull(clrType, nameof(clrType));
Check.NotNull(modelBuilder, nameof(modelBuilder));
Check.NotEmpty(name, nameof(name));
Check.NullButNotEmpty(schema, nameof(schema));
Check.NotNull(clrType, nameof(clrType));

var sequence = GetOrAddSequence(modelBuilder, name, schema);
var sequence = HasSequence(modelBuilder.Model, name, schema, ConfigurationSource.Explicit);
sequence.ClrType = clrType;

return new SequenceBuilder(sequence);
Expand Down Expand Up @@ -131,10 +124,6 @@ public static ModelBuilder HasSequence(
[CanBeNull] string schema,
[NotNull] Action<SequenceBuilder> builderAction)
{
Check.NotNull(clrType, nameof(clrType));
Check.NotNull(modelBuilder, nameof(modelBuilder));
Check.NotEmpty(name, nameof(name));
Check.NullButNotEmpty(schema, nameof(schema));
Check.NotNull(builderAction, nameof(builderAction));

builderAction(HasSequence(modelBuilder, clrType, name, schema));
Expand All @@ -156,10 +145,8 @@ public static SequenceBuilder HasSequence<T>(
[CanBeNull] string schema = null)
{
Check.NotNull(modelBuilder, nameof(modelBuilder));
Check.NotEmpty(name, nameof(name));
Check.NullButNotEmpty(schema, nameof(schema));

var sequence = GetOrAddSequence(modelBuilder, name, schema);
var sequence = HasSequence(modelBuilder.Model, name, schema, ConfigurationSource.Explicit);
sequence.ClrType = typeof(T);

return new SequenceBuilder(sequence);
Expand Down Expand Up @@ -194,42 +181,13 @@ public static ModelBuilder HasSequence<T>(
[CanBeNull] string schema,
[NotNull] Action<SequenceBuilder> builderAction)
{
Check.NotNull(modelBuilder, nameof(modelBuilder));
Check.NotEmpty(name, nameof(name));
Check.NullButNotEmpty(schema, nameof(schema));
Check.NotNull(builderAction, nameof(builderAction));

builderAction(HasSequence<T>(modelBuilder, name, schema));

return modelBuilder;
}

private static IMutableSequence GetOrAddSequence(ModelBuilder modelBuilder, string name, string schema)
{
var sequence = modelBuilder.Model.FindSequence(name, schema);
if (sequence != null)
{
((Sequence)sequence).UpdateConfigurationSource(ConfigurationSource.Explicit);
return sequence;
}

return modelBuilder.Model.AddSequence(name, schema);
}

private static IConventionSequence GetOrAddSequence(
IConventionModelBuilder modelBuilder, string name, string schema, bool fromDataAnnotation)
{
var sequence = modelBuilder.Metadata.FindSequence(name, schema);
if (sequence != null)
{
((Sequence)sequence).UpdateConfigurationSource(
fromDataAnnotation ? ConfigurationSource.DataAnnotation : ConfigurationSource.Convention);
return sequence;
}

return modelBuilder.Metadata.AddSequence(name, schema);
}

/// <summary>
/// Configures a database sequence when targeting a relational database.
/// </summary>
Expand All @@ -243,12 +201,26 @@ public static IConventionSequenceBuilder HasSequence(
[NotNull] string name,
[CanBeNull] string schema = null,
bool fromDataAnnotation = false)
=> HasSequence(
(IMutableModel)Check.NotNull(modelBuilder, nameof(modelBuilder)).Metadata,
name,
schema,
fromDataAnnotation ? ConfigurationSource.DataAnnotation : ConfigurationSource.Convention).Builder;

private static Sequence HasSequence(
IMutableModel model, string name, string schema, ConfigurationSource configurationSource)
{
Check.NotNull(modelBuilder, nameof(modelBuilder));
Check.NotEmpty(name, nameof(name));
Check.NullButNotEmpty(schema, nameof(schema));

return new SequenceBuilder((IMutableSequence)GetOrAddSequence(modelBuilder, name, schema, fromDataAnnotation));
var sequence = Sequence.FindSequence(model, name, schema);
if (sequence != null)
{
sequence.UpdateConfigurationSource(configurationSource);
return sequence;
}

return Sequence.AddSequence(model, name, schema, configurationSource);
}

/// <summary>
Expand Down Expand Up @@ -312,8 +284,6 @@ public static ModelBuilder HasDbFunction(
[NotNull] MethodInfo methodInfo,
[NotNull] Action<DbFunctionBuilder> builderAction)
{
Check.NotNull(modelBuilder, nameof(modelBuilder));
Check.NotNull(methodInfo, nameof(methodInfo));
Check.NotNull(builderAction, nameof(builderAction));

builderAction(HasDbFunction(modelBuilder, methodInfo));
Expand Down Expand Up @@ -347,7 +317,7 @@ public static IConventionDbFunctionBuilder HasDbFunction(
fromDataAnnotation ? ConfigurationSource.DataAnnotation : ConfigurationSource.Convention);
}

return new DbFunctionBuilder((IMutableDbFunction)dbFunction);
return dbFunction.Builder;
}

/// <summary>
Expand Down
44 changes: 25 additions & 19 deletions src/EFCore.Relational/Extensions/RelationalModelExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -263,7 +263,7 @@ public static IConventionDbFunction FindDbFunction([NotNull] this IConventionMod
=> (IConventionDbFunction)((IModel)model).FindDbFunction(method);

/// <summary>
/// Finds a <see cref="IDbFunction" /> that is mapped to the method represented by the given name.
/// Finds an <see cref="IDbFunction" /> that is mapped to the method represented by the given name.
/// </summary>
/// <param name="model"> The model to find the function in. </param>
/// <param name="name"> The model name of the function. </param>
Expand All @@ -274,7 +274,7 @@ public static IDbFunction FindDbFunction([NotNull] this IModel model, [NotNull]
Check.NotNull(name, nameof(name)));

/// <summary>
/// Finds a <see cref="IMutableDbFunction" /> that is mapped to the method represented by the given name.
/// Finds an <see cref="IMutableDbFunction" /> that is mapped to the method represented by the given name.
/// </summary>
/// <param name="model"> The model to find the function in. </param>
/// <param name="name"> The model name of the function. </param>
Expand All @@ -283,7 +283,7 @@ public static IMutableDbFunction FindDbFunction([NotNull] this IMutableModel mod
=> (IMutableDbFunction)((IModel)model).FindDbFunction(name);

/// <summary>
/// Finds a <see cref="IConventionDbFunction" /> that is mapped to the method represented by the given name.
/// Finds an <see cref="IConventionDbFunction" /> that is mapped to the method represented by the given name.
/// </summary>
/// <param name="model"> The model to find the function in. </param>
/// <param name="name"> The model name of the function. </param>
Expand All @@ -292,53 +292,59 @@ public static IConventionDbFunction FindDbFunction([NotNull] this IConventionMod
=> (IConventionDbFunction)((IModel)model).FindDbFunction(name);

/// <summary>
/// Either returns the existing <see cref="DbFunction" /> mapped to the given method
/// or creates a new function mapped to the method.
/// Creates an <see cref="IMutableDbFunction" /> mapped to the given method.
/// </summary>
/// <param name="model"> The model to add the function to. </param>
/// <param name="methodInfo"> The <see cref="MethodInfo" /> for the method that is mapped to the function. </param>
/// <returns> The <see cref="DbFunction" />. </returns>
/// <returns> The new <see cref="IMutableDbFunction" />. </returns>
public static IMutableDbFunction AddDbFunction([NotNull] this IMutableModel model, [NotNull] MethodInfo methodInfo)
=> DbFunction.AddDbFunction(
model, Check.NotNull(methodInfo, nameof(methodInfo)), ConfigurationSource.Explicit);

/// <summary>
/// Either returns the existing <see cref="DbFunction" /> mapped to the given method
/// or creates a new function mapped to the method.
/// Creates an <see cref="IConventionDbFunction" /> mapped to the given method.
/// </summary>
/// <param name="model"> The model to add the function to. </param>
/// <param name="methodInfo"> The <see cref="MethodInfo" /> for the method that is mapped to the function. </param>
/// <param name="fromDataAnnotation"> Indicates whether the configuration was specified using a data annotation. </param>
/// <returns> The <see cref="DbFunction" />. </returns>
/// <returns> The new <see cref="IConventionDbFunction" />. </returns>
public static IConventionDbFunction AddDbFunction(
[NotNull] this IConventionModel model, [NotNull] MethodInfo methodInfo, bool fromDataAnnotation = false)
=> DbFunction.AddDbFunction(
(IMutableModel)model, Check.NotNull(methodInfo, nameof(methodInfo)),
fromDataAnnotation ? ConfigurationSource.DataAnnotation : ConfigurationSource.Convention);

/// <summary>
/// Either returns the existing <see cref="DbFunction" /> mapped to the given method
/// or creates a new function mapped to the method.
/// Creates an <see cref="IMutableDbFunction" />.
/// </summary>
/// <param name="model"> The model to add the function to. </param>
/// <param name="name"> The model name of the function. </param>
/// <returns> The <see cref="DbFunction" />. </returns>
public static IMutableDbFunction AddDbFunction([NotNull] this IMutableModel model, [NotNull] string name)
/// <param name="returnType"> The function return type. </param>
/// <returns> The new <see cref="IMutableDbFunction" />. </returns>
public static IMutableDbFunction AddDbFunction(
[NotNull] this IMutableModel model,
[NotNull] string name,
[NotNull] Type returnType)
=> DbFunction.AddDbFunction(
model, Check.NotNull(name, nameof(name)), ConfigurationSource.Explicit);
model, Check.NotNull(name, nameof(name)), returnType, ConfigurationSource.Explicit);

/// <summary>
/// Either returns the existing <see cref="DbFunction" /> mapped to the given method
/// or creates a new function mapped to the method.
/// Creates an <see cref="IConventionDbFunction" />.
/// </summary>
/// <param name="model"> The model to add the function to. </param>
/// <param name="name"> The model name of the function. </param>
/// <param name="returnType"> The function return type. </param>
/// <param name="fromDataAnnotation"> Indicates whether the configuration was specified using a data annotation. </param>
/// <returns> The <see cref="DbFunction" />. </returns>
/// <returns> The new <see cref="IConventionDbFunction" />. </returns>
public static IConventionDbFunction AddDbFunction(
[NotNull] this IConventionModel model, [NotNull] string name, bool fromDataAnnotation = false)
[NotNull] this IConventionModel model,
[NotNull] string name,
[NotNull] Type returnType,
bool fromDataAnnotation = false)
=> DbFunction.AddDbFunction(
(IMutableModel)model, Check.NotNull(name, nameof(name)),
(IMutableModel)model,
Check.NotNull(name, nameof(name)),
returnType,
fromDataAnnotation ? ConfigurationSource.DataAnnotation : ConfigurationSource.Convention);

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,8 +80,8 @@ protected virtual void ValidateDbFunctions(
RelationalStrings.DbFunctionNameEmpty(methodInfo.DisplayName()));
}

if (dbFunction.TypeMapping == null &&
!(dbFunction.IsQueryable && model.FindEntityType(dbFunction.MethodInfo.ReturnType.GetGenericArguments()[0]) != null))
if (dbFunction.TypeMapping == null
&& dbFunction.QueryableEntityType == null)
{
throw new InvalidOperationException(
RelationalStrings.DbFunctionInvalidReturnType(
Expand Down
Loading

0 comments on commit f292936

Please sign in to comment.