Skip to content

Commit

Permalink
Don't remove default table mapping for TVF return entity types.
Browse files Browse the repository at this point in the history
Preserve null mappings in the model snapshot.
Add non-generic ToSqlQuery overload.
Output Fluent API for ToSqlQuery in the model snapshot.
Fix SetFunctionName setting incorrect annotation.

Fixes #23408
  • Loading branch information
AndriySvyryd committed Jun 4, 2021
1 parent b141038 commit e3a16a5
Show file tree
Hide file tree
Showing 10 changed files with 187 additions and 56 deletions.
56 changes: 44 additions & 12 deletions src/EFCore.Design/Migrations/Design/CSharpSnapshotGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -825,28 +825,35 @@ protected virtual void GenerateEntityTypeAnnotations(
|| entityType.BaseType == null)
{
var tableName = (string?)tableNameAnnotation?.Value ?? entityType.GetTableName();
if (tableName != null)
if (tableName != null
|| tableNameAnnotation != null)
{
stringBuilder
.AppendLine()
.Append(builderName)
.Append(".ToTable(")
.Append(Code.Literal(tableName));
.Append(Code.UnknownLiteral(tableName));
if (tableNameAnnotation != null)
{
annotations.Remove(tableNameAnnotation.Name);
}

var isExcludedAnnotation = annotations.Find(RelationalAnnotationNames.IsTableExcludedFromMigrations);
var schemaAnnotation = annotations.Find(RelationalAnnotationNames.Schema);
if (schemaAnnotation?.Value != null)
if (schemaAnnotation != null)
{
stringBuilder
.Append(", ")
.Append(Code.Literal((string)schemaAnnotation.Value));
annotations.Remove(schemaAnnotation.Name);
.Append(", ");

if (schemaAnnotation.Value == null
&& ((bool?)isExcludedAnnotation?.Value) != true)
{
stringBuilder.Append("(string)");
}

stringBuilder.Append(Code.UnknownLiteral(schemaAnnotation.Value));
}

var isExcludedAnnotation = annotations.Find(RelationalAnnotationNames.IsTableExcludedFromMigrations);
if (isExcludedAnnotation != null)
{
if (((bool?)isExcludedAnnotation.Value) == true)
Expand All @@ -870,19 +877,21 @@ protected virtual void GenerateEntityTypeAnnotations(
stringBuilder.AppendLine(");");
}
}
annotations.Remove(RelationalAnnotationNames.Schema);

var viewNameAnnotation = annotations.Find(RelationalAnnotationNames.ViewName);
if (viewNameAnnotation?.Value != null
|| entityType.BaseType == null)
{
var viewName = (string?)viewNameAnnotation?.Value ?? entityType.GetViewName();
if (viewName != null)
if (viewName != null
|| viewNameAnnotation != null)
{
stringBuilder
.AppendLine()
.Append(builderName)
.Append(".ToView(")
.Append(Code.Literal(viewName));
.Append(Code.UnknownLiteral(viewName));
if (viewNameAnnotation != null)
{
annotations.Remove(viewNameAnnotation.Name);
Expand All @@ -900,25 +909,48 @@ protected virtual void GenerateEntityTypeAnnotations(
stringBuilder.AppendLine(");");
}
}
annotations.Remove(RelationalAnnotationNames.ViewSchema);
annotations.Remove(RelationalAnnotationNames.ViewDefinitionSql);

var functionNameAnnotation = annotations.Find(RelationalAnnotationNames.FunctionName);
if (functionNameAnnotation?.Value != null
|| entityType.BaseType == null)
{
var functionName = (string?)functionNameAnnotation?.Value ?? entityType.GetFunctionName();
if (functionName != null)
if (functionName != null
|| functionNameAnnotation != null)
{
stringBuilder
.AppendLine()
.Append(builderName)
.Append(".ToFunction(")
.Append(Code.Literal(functionName));
.Append(Code.UnknownLiteral(functionName))
.AppendLine(");");
if (functionNameAnnotation != null)
{
annotations.Remove(functionNameAnnotation.Name);
}
}
}

stringBuilder.AppendLine(");");
var sqlQueryAnnotation = annotations.Find(RelationalAnnotationNames.SqlQuery);
if (sqlQueryAnnotation?.Value != null
|| entityType.BaseType == null)
{
var sqlQuery = (string?)sqlQueryAnnotation?.Value ?? entityType.GetSqlQuery();
if (sqlQuery != null
|| sqlQueryAnnotation != null)
{
stringBuilder
.AppendLine()
.Append(builderName)
.Append(".ToSqlQuery(")
.Append(Code.UnknownLiteral(sqlQuery))
.AppendLine(");");
if (sqlQueryAnnotation != null)
{
annotations.Remove(sqlQueryAnnotation.Name);
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,7 @@ private static IEnumerable<IAnnotatable> GetAnnotatables(IModel model)
private IEnumerable<string> GetAnnotationNamespaces(IEnumerable<IAnnotatable> items)
=> items.SelectMany(
i => Dependencies.AnnotationCodeGenerator.FilterIgnoredAnnotations(i.GetAnnotations())
.Where(a => a.Value != null)
.Select(a => new { Annotatable = i, Annotation = a })
.SelectMany(a => GetProviderType(a.Annotatable, a.Annotation.Value!.GetType()).GetNamespaces()));

Expand Down
14 changes: 7 additions & 7 deletions src/EFCore.Relational/Design/AnnotationCodeGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -54,9 +54,7 @@ public AnnotationCodeGenerator(AnnotationCodeGeneratorDependencies dependencies)
/// <inheritdoc />
public virtual IEnumerable<IAnnotation> FilterIgnoredAnnotations(IEnumerable<IAnnotation> annotations)
=> annotations.Where(
a => !(
a.Value is null
|| CoreAnnotationNames.AllNames.Contains(a.Name)
a => !(CoreAnnotationNames.AllNames.Contains(a.Name)
|| _ignoredRelationalAnnotations.Contains(a.Name)));

/// <inheritdoc />
Expand Down Expand Up @@ -676,12 +674,14 @@ private static void GenerateSimpleFluentApiCall(
string methodName,
List<MethodCallCodeFragment> methodCallCodeFragments)
{
if (annotations.TryGetValue(annotationName, out var annotation)
&& annotation.Value is object annotationValue)
if (annotations.TryGetValue(annotationName, out var annotation))
{
annotations.Remove(annotationName);
methodCallCodeFragments.Add(
new MethodCallCodeFragment(methodName, annotationValue));
if (annotation.Value is object annotationValue)
{
methodCallCodeFragments.Add(
new MethodCallCodeFragment(methodName, annotationValue));
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -660,10 +660,9 @@ public static bool CanSetViewSchema(
/// <param name="entityTypeBuilder"> The builder for the entity type being configured. </param>
/// <param name="query"> The SQL query that will provide the underlying data for the entity type. </param>
/// <returns> The same builder instance so that multiple calls can be chained. </returns>
public static EntityTypeBuilder<TEntity> ToSqlQuery<TEntity>(
this EntityTypeBuilder<TEntity> entityTypeBuilder,
public static EntityTypeBuilder ToSqlQuery(
this EntityTypeBuilder entityTypeBuilder,
string query)
where TEntity : class
{
Check.NotNull(query, nameof(query));

Expand All @@ -672,6 +671,18 @@ public static EntityTypeBuilder<TEntity> ToSqlQuery<TEntity>(
return entityTypeBuilder;
}

/// <summary>
/// Configures a SQL string used to provide data for the entity type.
/// </summary>
/// <param name="entityTypeBuilder"> The builder for the entity type being configured. </param>
/// <param name="query"> The SQL query that will provide the underlying data for the entity type. </param>
/// <returns> The same builder instance so that multiple calls can be chained. </returns>
public static EntityTypeBuilder<TEntity> ToSqlQuery<TEntity>(
this EntityTypeBuilder<TEntity> entityTypeBuilder,
string query)
where TEntity : class
=> (EntityTypeBuilder<TEntity>)ToSqlQuery((EntityTypeBuilder)entityTypeBuilder, query);

/// <summary>
/// Configures a SQL string used to provide data for the entity type.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -547,7 +547,7 @@ public static void SetFunctionName(this IMutableEntityType entityType, string? n
string? name,
bool fromDataAnnotation = false)
=> (string?)entityType.SetAnnotation(
RelationalAnnotationNames.ViewName,
RelationalAnnotationNames.FunctionName,
Check.NullButNotEmpty(name, nameof(name)),
fromDataAnnotation)?.Value;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,11 +90,6 @@ private void ProcessDbFunctionAdded(

entityType = entityTypeBuilder.Metadata;
}

if (entityType.GetFunctionName() == null)
{
entityTypeBuilder.ToTable(null);
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -70,19 +70,37 @@ public void Test_new_annotations_handled_for_entity_types()
RelationalAnnotationNames.ViewColumnMappings,
RelationalAnnotationNames.SqlQueryColumnMappings,
RelationalAnnotationNames.FunctionColumnMappings,
RelationalAnnotationNames.DefaultColumnMappings,
RelationalAnnotationNames.TableMappings,
RelationalAnnotationNames.ViewMappings,
RelationalAnnotationNames.FunctionMappings,
RelationalAnnotationNames.SqlQueryMappings,
RelationalAnnotationNames.DefaultMappings,
RelationalAnnotationNames.ForeignKeyMappings,
RelationalAnnotationNames.TableIndexMappings,
RelationalAnnotationNames.UniqueConstraintMappings,
RelationalAnnotationNames.RelationalOverrides,
RelationalAnnotationNames.DefaultValueSql,
RelationalAnnotationNames.ComputedColumnSql,
RelationalAnnotationNames.DefaultValue,
RelationalAnnotationNames.Name,
#pragma warning disable CS0618 // Type or member is obsolete
RelationalAnnotationNames.SequencePrefix,
#pragma warning restore CS0618 // Type or member is obsolete
RelationalAnnotationNames.Sequences,
RelationalAnnotationNames.CheckConstraints,
RelationalAnnotationNames.DefaultSchema,
RelationalAnnotationNames.Filter,
#pragma warning disable CS0618 // Type or member is obsolete
RelationalAnnotationNames.DbFunction,
#pragma warning restore CS0618 // Type or member is obsolete
RelationalAnnotationNames.DbFunctions,
RelationalAnnotationNames.MaxIdentifierLength,
RelationalAnnotationNames.IsFixedLength,
RelationalAnnotationNames.Collation
RelationalAnnotationNames.Collation,
RelationalAnnotationNames.IsStored,
RelationalAnnotationNames.RelationalModel,
RelationalAnnotationNames.ModelDependencies
};

// Add a line here if the code generator is supposed to handle this annotation
Expand Down Expand Up @@ -140,18 +158,22 @@ public void Test_new_annotations_handled_for_entity_types()
},
{
RelationalAnnotationNames.FunctionName,
(null, "")
(null, _nl + "modelBuilder." + nameof(RelationalEntityTypeBuilderExtensions.ToFunction) + @"(null);" + _nl)
},
{
RelationalAnnotationNames.SqlQuery,
(null, "")
(null, _nl + "modelBuilder." + nameof(RelationalEntityTypeBuilderExtensions.ToSqlQuery) + @"(null);" + _nl)
},
{
CoreAnnotationNames.PropertyAccessMode,
(null, _toTable)
}
};

MissingAnnotationCheck(
b => b.Entity<WithAnnotations>().Metadata,
notForEntityType, forEntityType,
_toTable,
a => _toTable,
(g, m, b) => g.TestGenerateEntityTypeAnnotations("modelBuilder", (IEntityType)m, b));
}

Expand All @@ -176,20 +198,41 @@ public void Test_new_annotations_handled_for_properties()
CoreAnnotationNames.AmbiguousNavigations,
CoreAnnotationNames.DuplicateServiceProperties,
RelationalAnnotationNames.TableName,
RelationalAnnotationNames.IsTableExcludedFromMigrations,
RelationalAnnotationNames.ViewName,
RelationalAnnotationNames.Schema,
RelationalAnnotationNames.ViewSchema,
RelationalAnnotationNames.ViewDefinitionSql,
RelationalAnnotationNames.FunctionName,
RelationalAnnotationNames.SqlQuery,
RelationalAnnotationNames.DefaultSchema,
RelationalAnnotationNames.DefaultMappings,
RelationalAnnotationNames.TableColumnMappings,
RelationalAnnotationNames.ViewColumnMappings,
RelationalAnnotationNames.SqlQueryColumnMappings,
RelationalAnnotationNames.FunctionColumnMappings,
RelationalAnnotationNames.DefaultColumnMappings,
RelationalAnnotationNames.TableMappings,
RelationalAnnotationNames.ViewMappings,
RelationalAnnotationNames.FunctionMappings,
RelationalAnnotationNames.SqlQueryMappings,
RelationalAnnotationNames.ForeignKeyMappings,
RelationalAnnotationNames.TableIndexMappings,
RelationalAnnotationNames.UniqueConstraintMappings,
RelationalAnnotationNames.Name,
RelationalAnnotationNames.Sequences,
#pragma warning disable CS0618 // Type or member is obsolete
RelationalAnnotationNames.SequencePrefix,
#pragma warning restore CS0618 // Type or member is obsolete
RelationalAnnotationNames.CheckConstraints,
RelationalAnnotationNames.Filter,
#pragma warning disable CS0618 // Type or member is obsolete
RelationalAnnotationNames.DbFunction,
#pragma warning restore CS0618 // Type or member is obsolete
RelationalAnnotationNames.DbFunctions,
RelationalAnnotationNames.MaxIdentifierLength
RelationalAnnotationNames.MaxIdentifierLength,
RelationalAnnotationNames.RelationalModel,
RelationalAnnotationNames.ModelDependencies
};

var columnMapping =
Expand All @@ -201,6 +244,7 @@ public void Test_new_annotations_handled_for_properties()
{
{ CoreAnnotationNames.MaxLength, (256, $@"{_nl}.{nameof(PropertyBuilder.HasMaxLength)}(256){columnMapping}") },
{ CoreAnnotationNames.Precision, (4, $@"{_nl}.{nameof(PropertyBuilder.HasPrecision)}(4){columnMapping}") },
{ CoreAnnotationNames.Scale, (null, $@"{columnMapping}") },
{ CoreAnnotationNames.Unicode, (false, $@"{_nl}.{nameof(PropertyBuilder.IsUnicode)}(false){columnMapping}") },
{
CoreAnnotationNames.ValueConverter, (new ValueConverter<int, long>(v => v, v => (int)v),
Expand Down Expand Up @@ -242,21 +286,25 @@ public void Test_new_annotations_handled_for_properties()
RelationalAnnotationNames.Collation,
("Some Collation",
$@"{columnMapping}{_nl}.{nameof(RelationalPropertyBuilderExtensions.UseCollation)}(""Some Collation"")")
},
{
RelationalAnnotationNames.IsStored,
(null, $@"{columnMapping}{_nl}.HasAnnotation(""{RelationalAnnotationNames.IsStored}"", null)")
}
};

MissingAnnotationCheck(
b => b.Entity<WithAnnotations>().Property(e => e.Id).Metadata,
notForProperty, forProperty,
$"{columnMapping}",
a => $"{columnMapping}",
(g, m, b) => g.TestGeneratePropertyAnnotations((IProperty)m, b));
}

private static void MissingAnnotationCheck(
Func<ModelBuilder, IMutableAnnotatable> createMetadataItem,
HashSet<string> invalidAnnotations,
Dictionary<string, (object Value, string Expected)> validAnnotations,
string generationDefault,
Func<string, string> generationDefault,
Action<TestCSharpSnapshotGenerator, IMutableAnnotatable, IndentedStringBuilder> test)
{
var sqlServerTypeMappingSource = new SqlServerTypeMappingSource(
Expand Down Expand Up @@ -314,7 +362,7 @@ private static void MissingAnnotationCheck(
Assert.Equal(
validAnnotations.ContainsKey(annotationName)
? validAnnotations[annotationName].Expected
: generationDefault,
: generationDefault(annotationName),
sb.ToString());
}
catch (Exception e)
Expand Down
Loading

0 comments on commit e3a16a5

Please sign in to comment.