Skip to content

Commit

Permalink
Store all functions in one annotation
Browse files Browse the repository at this point in the history
Allow to create functions without a method

Part of #20051
  • Loading branch information
AndriySvyryd committed Feb 27, 2020
1 parent daa1ed2 commit f715c9d
Show file tree
Hide file tree
Showing 14 changed files with 431 additions and 208 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -55,14 +55,14 @@ public virtual void Generate(string builderName, IModel model, IndentedStringBui

var annotations = model.GetAnnotations().ToList();

IgnoreAnnotationTypes(annotations, RelationalAnnotationNames.DbFunction);
IgnoreAnnotations(
annotations,
ChangeDetector.SkipDetectChangesAnnotation,
CoreAnnotationNames.ChangeTrackingStrategy,
CoreAnnotationNames.OwnedTypes,
RelationalAnnotationNames.CheckConstraints,
RelationalAnnotationNames.Sequences,
RelationalAnnotationNames.DbFunctions,
RelationalAnnotationNames.Tables,
RelationalAnnotationNames.Views);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,7 @@ private IEnumerable<string> GetAnnotationNamespaces(IEnumerable<IAnnotatable> it
CoreAnnotationNames.QueryFilter,
RelationalAnnotationNames.CheckConstraints,
RelationalAnnotationNames.Sequences,
RelationalAnnotationNames.DbFunctions,
RelationalAnnotationNames.Tables,
RelationalAnnotationNames.TableMappings,
RelationalAnnotationNames.TableColumnMappings,
Expand All @@ -251,18 +252,12 @@ private IEnumerable<string> GetAnnotationNamespaces(IEnumerable<IAnnotatable> it
RelationalAnnotationNames.ViewColumnMappings
};

var ignoredAnnotationTypes = new List<string>
{
RelationalAnnotationNames.DbFunction
};

return items.SelectMany(
i => i.GetAnnotations().Select(
a => new { Annotatable = i, Annotation = a })
.Where(
a => a.Annotation.Value != null
&& !ignoredAnnotations.Contains(a.Annotation.Name)
&& !ignoredAnnotationTypes.Any(p => a.Annotation.Name.StartsWith(p, StringComparison.Ordinal)))
&& !ignoredAnnotations.Contains(a.Annotation.Name))
.SelectMany(a => GetProviderType(a.Annotatable, a.Annotation.Value.GetType()).GetNamespaces()));
}

Expand Down
112 changes: 89 additions & 23 deletions src/EFCore.Relational/Extensions/RelationalModelExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -219,7 +219,7 @@ public static IConventionSequence AddSequence(
/// </returns>
public static IMutableSequence RemoveSequence(
[NotNull] this IMutableModel model, [NotNull] string name, [CanBeNull] string schema = null)
=> Sequence.RemoveSequence(model, name, schema);
=> Sequence.RemoveSequence(Check.NotNull(model, nameof(model)), name, schema);

/// <summary>
/// Removes the <see cref="IConventionSequence" /> with the given name.
Expand All @@ -233,28 +233,28 @@ public static IMutableSequence RemoveSequence(
/// </returns>
public static IConventionSequence RemoveSequence(
[NotNull] this IConventionModel model, [NotNull] string name, [CanBeNull] string schema = null)
=> (IConventionSequence)((IMutableModel)model).RemoveSequence(name, schema);
=> Sequence.RemoveSequence((IMutableModel)Check.NotNull(model, nameof(model)), name, schema);

/// <summary>
/// Returns all <see cref="ISequence" />s contained in the model.
/// </summary>
/// <param name="model"> The model to get the sequences in. </param>
public static IEnumerable<ISequence> GetSequences([NotNull] this IModel model)
=> Sequence.GetSequences(model);
=> Sequence.GetSequences(Check.NotNull(model, nameof(model)));

/// <summary>
/// Returns all <see cref="IMutableSequence" />s contained in the model.
/// </summary>
/// <param name="model"> The model to get the sequences in. </param>
public static IEnumerable<IMutableSequence> GetSequences([NotNull] this IMutableModel model)
=> (IEnumerable<IMutableSequence>)((IModel)model).GetSequences();
=> Sequence.GetSequences(Check.NotNull(model, nameof(model)));

/// <summary>
/// Returns all <see cref="IConventionSequence" />s contained in the model.
/// </summary>
/// <param name="model"> The model to get the sequences in. </param>
public static IEnumerable<IConventionSequence> GetSequences([NotNull] this IConventionModel model)
=> (IEnumerable<IConventionSequence>)((IModel)model).GetSequences();
=> Sequence.GetSequences(Check.NotNull(model, nameof(model)));

/// <summary>
/// Finds a <see cref="IDbFunction" /> that is mapped to the method represented by the given <see cref="MethodInfo" />.
Expand All @@ -263,14 +263,9 @@ public static IEnumerable<IConventionSequence> GetSequences([NotNull] this IConv
/// <param name="method"> The <see cref="MethodInfo" /> for the method that is mapped to the function. </param>
/// <returns> The <see cref="IDbFunction" /> or <c>null</c> if the method is not mapped. </returns>
public static IDbFunction FindDbFunction([NotNull] this IModel model, [NotNull] MethodInfo method)
{
Check.NotNull(model, nameof(model));
Check.NotNull(method, nameof(method));

return DbFunction.FindDbFunction(
=> DbFunction.FindDbFunction(
Check.NotNull(model, nameof(model)),
Check.NotNull(method, nameof(method)));
}

/// <summary>
/// Finds a <see cref="IMutableDbFunction" /> that is mapped to the method represented by the given <see cref="MethodInfo" />.
Expand All @@ -290,6 +285,35 @@ public static IMutableDbFunction FindDbFunction([NotNull] this IMutableModel mod
public static IConventionDbFunction FindDbFunction([NotNull] this IConventionModel model, [NotNull] MethodInfo method)
=> (IConventionDbFunction)((IModel)model).FindDbFunction(method);

/// <summary>
/// Finds a <see cref="IDbFunction" /> that is mapped to the method represented by the given <see cref="MethodInfo" />.
/// </summary>
/// <param name="model"> The model to find the function in. </param>
/// <param name="name"> The model name of the function. </param>
/// <returns> The <see cref="IDbFunction" /> or <c>null</c> if the method is not mapped. </returns>
public static IDbFunction FindDbFunction([NotNull] this IModel model, [NotNull] string name)
=> DbFunction.FindDbFunction(
Check.NotNull(model, nameof(model)),
Check.NotNull(name, nameof(name)));

/// <summary>
/// Finds a <see cref="IMutableDbFunction" /> that is mapped to the method represented by the given <see cref="MethodInfo" />.
/// </summary>
/// <param name="model"> The model to find the function in. </param>
/// <param name="name"> The model name of the function. </param>
/// <returns> The <see cref="IMutableDbFunction" /> or <c>null</c> if the method is not mapped. </returns>
public static IMutableDbFunction FindDbFunction([NotNull] this IMutableModel model, [NotNull] string name)
=> (IMutableDbFunction)((IModel)model).FindDbFunction(name);

/// <summary>
/// Finds a <see cref="IConventionDbFunction" /> that is mapped to the method represented by the given <see cref="MethodInfo" />.
/// </summary>
/// <param name="model"> The model to find the function in. </param>
/// <param name="name"> The model name of the function. </param>
/// <returns> The <see cref="IConventionDbFunction" /> or <c>null</c> if the method is not mapped. </returns>
public static IConventionDbFunction FindDbFunction([NotNull] this IConventionModel model, [NotNull] string name)
=> (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.
Expand All @@ -298,8 +322,8 @@ public static IConventionDbFunction FindDbFunction([NotNull] this IConventionMod
/// <param name="methodInfo"> The <see cref="MethodInfo" /> for the method that is mapped to the function. </param>
/// <returns> The <see cref="DbFunction" />. </returns>
public static DbFunction AddDbFunction([NotNull] this IMutableModel model, [NotNull] MethodInfo methodInfo)
=> new DbFunction(
Check.NotNull(methodInfo, nameof(methodInfo)), model, ConfigurationSource.Explicit);
=> DbFunction.AddDbFunction(
model, Check.NotNull(methodInfo, nameof(methodInfo)), ConfigurationSource.Explicit);

/// <summary>
/// Either returns the existing <see cref="DbFunction" /> mapped to the given method
Expand All @@ -311,9 +335,34 @@ public static DbFunction AddDbFunction([NotNull] this IMutableModel model, [NotN
/// <returns> The <see cref="DbFunction" />. </returns>
public static IConventionDbFunction AddDbFunction(
[NotNull] this IConventionModel model, [NotNull] MethodInfo methodInfo, bool fromDataAnnotation = false)
=> new DbFunction(
Check.NotNull(methodInfo, nameof(methodInfo)), (IMutableModel)model,
fromDataAnnotation ? ConfigurationSource.DataAnnotation : ConfigurationSource.Convention);
=> 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.
/// </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 DbFunction AddDbFunction([NotNull] this IMutableModel model, [NotNull] string name)
=> DbFunction.AddDbFunction(
model, Check.NotNull(name, nameof(name)), ConfigurationSource.Explicit);

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

/// <summary>
/// Removes the <see cref="IMutableDbFunction" /> that is mapped to the method represented by the given
Expand All @@ -323,14 +372,9 @@ public static IConventionDbFunction AddDbFunction(
/// <param name="method"> The <see cref="MethodInfo" /> for the method that is mapped to the function. </param>
/// <returns> The removed <see cref="IMutableDbFunction" /> or <c>null</c> if the method is not mapped. </returns>
public static IMutableDbFunction RemoveDbFunction([NotNull] this IMutableModel model, [NotNull] MethodInfo method)
{
Check.NotNull(model, nameof(model));
Check.NotNull(method, nameof(method));

return DbFunction.RemoveDbFunction(
=> DbFunction.RemoveDbFunction(
Check.NotNull(model, nameof(model)),
Check.NotNull(method, nameof(method)));
}

/// <summary>
/// Removes the <see cref="IConventionDbFunction" /> that is mapped to the method represented by the given
Expand All @@ -342,12 +386,34 @@ public static IMutableDbFunction RemoveDbFunction([NotNull] this IMutableModel m
public static IConventionDbFunction RemoveDbFunction([NotNull] this IConventionModel model, [NotNull] MethodInfo method)
=> (IConventionDbFunction)((IMutableModel)model).RemoveDbFunction(method);

/// <summary>
/// Removes the <see cref="IMutableDbFunction" /> that is mapped to the method represented by the given
/// <see cref="MethodInfo" />.
/// </summary>
/// <param name="model"> The model to find the function in. </param>
/// <param name="name"> The model name of the function. </param>
/// <returns> The removed <see cref="IMutableDbFunction" /> or <c>null</c> if the method is not mapped. </returns>
public static IMutableDbFunction RemoveDbFunction([NotNull] this IMutableModel model, [NotNull] string name)
=> DbFunction.RemoveDbFunction(
Check.NotNull(model, nameof(model)),
Check.NotNull(name, nameof(name)));

/// <summary>
/// Removes the <see cref="IConventionDbFunction" /> that is mapped to the method represented by the given
/// <see cref="MethodInfo" />.
/// </summary>
/// <param name="model"> The model to find the function in. </param>
/// <param name="name"> The model name of the function. </param>
/// <returns> The removed <see cref="IConventionDbFunction" /> or <c>null</c> if the method is not mapped. </returns>
public static IConventionDbFunction RemoveDbFunction([NotNull] this IConventionModel model, [NotNull] string name)
=> (IConventionDbFunction)((IMutableModel)model).RemoveDbFunction(name);

/// <summary>
/// Returns all <see cref="IDbFunction" />s contained in the model.
/// </summary>
/// <param name="model"> The model to get the functions in. </param>
public static IEnumerable<IDbFunction> GetDbFunctions([NotNull] this IModel model)
=> DbFunction.GetDbFunctions(model.AsModel());
=> DbFunction.GetDbFunctions(model);

/// <summary>
/// Returns all <see cref="IMutableDbFunction" />s contained in the model.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,8 +87,8 @@ public override ConventionSet CreateConventionSet()

var dbFunctionAttributeConvention = new RelationalDbFunctionAttributeConvention(Dependencies, RelationalDependencies);
conventionSet.ModelInitializedConventions.Add(dbFunctionAttributeConvention);
conventionSet.ModelAnnotationChangedConventions.Add(dbFunctionAttributeConvention);

conventionSet.ModelFinalizingConventions.Add(dbFunctionAttributeConvention);
conventionSet.ModelFinalizingConventions.Add(tableNameFromDbSetConvention);
conventionSet.ModelFinalizingConventions.Add(storeGenerationConvention);
conventionSet.ModelFinalizingConventions.Add(new SharedTableConvention(Dependencies, RelationalDependencies));
Expand Down
Original file line number Diff line number Diff line change
@@ -1,21 +1,19 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;
using System.Linq;
using System.Reflection;
using JetBrains.Annotations;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using Microsoft.EntityFrameworkCore.Metadata.Conventions.Infrastructure;
using Microsoft.EntityFrameworkCore.Utilities;

namespace Microsoft.EntityFrameworkCore.Metadata.Conventions
{
/// <summary>
/// A convention that configures model function mappings based on public static methods on the context marked with
/// <see cref="DbFunctionAttribute" />.
/// </summary>
public class RelationalDbFunctionAttributeConvention : IModelInitializedConvention, IModelAnnotationChangedConvention
public class RelationalDbFunctionAttributeConvention : IModelInitializedConvention, IModelFinalizingConvention
{
/// <summary>
/// Creates a new instance of <see cref="RelationalDbFunctionAttributeConvention" />.
Expand Down Expand Up @@ -63,29 +61,12 @@ public virtual void ProcessModelInitialized(
}
}

/// <summary>
/// Called after an annotation is changed on an model.
/// </summary>
/// <param name="modelBuilder"> The builder for the model. </param>
/// <param name="name"> The annotation name. </param>
/// <param name="annotation"> The new annotation. </param>
/// <param name="oldAnnotation"> The old annotation. </param>
/// <param name="context"> Additional information associated with convention execution. </param>
public virtual void ProcessModelAnnotationChanged(
IConventionModelBuilder modelBuilder,
string name,
IConventionAnnotation annotation,
IConventionAnnotation oldAnnotation,
IConventionContext<IConventionAnnotation> context)
/// <inheritdoc />
public virtual void ProcessModelFinalizing(IConventionModelBuilder modelBuilder, IConventionContext<IConventionModelBuilder> context)
{
Check.NotNull(modelBuilder, nameof(modelBuilder));
Check.NotNull(name, nameof(name));

if (name.StartsWith(RelationalAnnotationNames.DbFunction, StringComparison.Ordinal)
&& annotation?.Value != null
&& oldAnnotation == null)
foreach (var function in modelBuilder.Metadata.GetDbFunctions())
{
ProcessDbFunctionAdded(new DbFunctionBuilder((IMutableDbFunction)annotation.Value), context);
ProcessDbFunctionAdded(function.Builder, context);
}
}

Expand All @@ -98,10 +79,15 @@ protected virtual void ProcessDbFunctionAdded(
[NotNull] IConventionDbFunctionBuilder dbFunctionBuilder, [NotNull] IConventionContext context)
{
var methodInfo = dbFunctionBuilder.Metadata.MethodInfo;
var dbFunctionAttribute = methodInfo.GetCustomAttributes<DbFunctionAttribute>().SingleOrDefault();

dbFunctionBuilder.HasName(dbFunctionAttribute?.Name ?? methodInfo.Name);
dbFunctionBuilder.HasSchema(dbFunctionAttribute?.Schema);
var dbFunctionAttribute = methodInfo?.GetCustomAttributes<DbFunctionAttribute>().SingleOrDefault();
if (dbFunctionAttribute != null)
{
dbFunctionBuilder.HasName(dbFunctionAttribute.Name, fromDataAnnotation: true);
if (dbFunctionAttribute.Schema != null)
{
dbFunctionBuilder.HasSchema(dbFunctionAttribute.Schema, fromDataAnnotation: true);
}
}
}
}
}
5 changes: 5 additions & 0 deletions src/EFCore.Relational/Metadata/IDbFunction.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,11 @@ public interface IDbFunction
/// </summary>
string Schema { get; }

/// <summary>
/// The name of the function in the model.
/// </summary>
string ModelName { get; }

/// <summary>
/// The <see cref="IModel" /> in which this function is defined.
/// </summary>
Expand Down
Loading

0 comments on commit f715c9d

Please sign in to comment.