Skip to content

Commit

Permalink
Add DbFunctionParameter support for DbFunctions
Browse files Browse the repository at this point in the history
  • Loading branch information
pmiddleton committed Apr 8, 2019
1 parent 5c3860f commit 52746ef
Show file tree
Hide file tree
Showing 17 changed files with 525 additions and 20 deletions.
12 changes: 12 additions & 0 deletions src/EFCore.Relational/Infrastructure/RelationalModelValidator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,18 @@ protected virtual void ValidateDbFunctions([NotNull] IModel model, DiagnosticsLo
parameter.ParameterType.ShortDisplayName()));
}
}

foreach (var parameter in dbFunction.Parameters)
{
if(RelationalDependencies.TypeMappingSource.FindMapping(parameter.FunctionType) == null)
{
throw new InvalidOperationException(
RelationalStrings.DbFunctionInvalidParameterType(
parameter.Name,
methodInfo.DisplayName(),
parameter.FunctionType.ShortDisplayName()));
}
}
}
}
}
Expand Down
26 changes: 26 additions & 0 deletions src/EFCore.Relational/Metadata/Builders/DbFunctionBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,11 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Linq.Expressions;
using JetBrains.Annotations;
using Microsoft.EntityFrameworkCore.Diagnostics;
using Microsoft.EntityFrameworkCore.Internal;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata.Internal;
using Microsoft.EntityFrameworkCore.Utilities;
Expand Down Expand Up @@ -90,6 +93,29 @@ public virtual DbFunctionBuilder HasTranslation([NotNull] Func<IReadOnlyCollecti
return this;
}


/// <summary>
/// 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 DbFunctionParameterBuilder HasParameter([NotNull] string name)
{
return new DbFunctionParameterBuilder((DbFunctionParameter)FindParameter(name));
}

private IDbFunctionParameter FindParameter(string name)
{
var parameter = _builder.Metadata.Parameters.SingleOrDefault(funcParam => string.Compare(funcParam.Name, name, StringComparison.OrdinalIgnoreCase) == 0);

if (parameter == null)
{
throw new ArgumentException(
RelationalStrings.DbFunctionInvalidParameterName(name, _builder.Metadata.MethodInfo.DisplayName()));
}

return parameter;
}

#region Hidden System.Object members

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// 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.Collections.Generic;
using System.Text;
using JetBrains.Annotations;
using Microsoft.EntityFrameworkCore.Metadata.Internal;
using Microsoft.EntityFrameworkCore.Utilities;

namespace Microsoft.EntityFrameworkCore.Metadata.Builders
{
public class DbFunctionParameterBuilder
{
private readonly InternalDbFunctionParameterBuilder _builder;

public DbFunctionParameterBuilder(DbFunctionParameter dbFunctionParameter)
{
_builder = new InternalDbFunctionParameterBuilder(dbFunctionParameter);
}

/// <summary>
/// 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 DbFunctionParameterBuilder HasName([NotNull] string name)
{
Check.NotNull(name, nameof(name));

_builder.HasName(name);

return this;
}

/// <summary>
/// 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 DbFunctionParameterBuilder HasNullabilityPropagation(bool supportsNullabilityPropagation)
{
_builder.HasNullabilityPropagation(supportsNullabilityPropagation);

return this;
}

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,8 @@ public override ConventionSet CreateConventionSet()
conventionSet.PropertyAddedConventions.Add(relationalColumnAttributeConvention);

var sharedTableConvention = new SharedTableConvention(logger);
var dbFunctionTypeMappingConvention = new DbFunctionTypeMappingConvention(RelationalDependencies.TypeMappingSource);


var discriminatorConvention = new DiscriminatorConvention(logger);
conventionSet.EntityTypeAddedConventions.Add(new RelationalTableAttributeConvention(logger));
Expand All @@ -85,6 +87,7 @@ public override ConventionSet CreateConventionSet()
conventionSet.PropertyAnnotationChangedConventions.Add((RelationalValueGeneratorConvention)valueGeneratorConvention);

conventionSet.ModelBuiltConventions.Add(sharedTableConvention);
conventionSet.ModelBuiltConventions.Add(dbFunctionTypeMappingConvention);

conventionSet.ModelAnnotationChangedConventions.Add(new RelationalDbFunctionConvention(logger));

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// 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.Collections.Generic;
using System.Linq;
using System.Text;
using JetBrains.Annotations;
using Microsoft.EntityFrameworkCore.Metadata.Internal;
using Microsoft.EntityFrameworkCore.Storage;

namespace Microsoft.EntityFrameworkCore.Metadata.Conventions.Internal
{
/// <summary>
/// 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 DbFunctionTypeMappingConvention : IModelBuiltConvention
{
private readonly IRelationalTypeMappingSource _typeMappingSource;

/// <summary>
/// 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 DbFunctionTypeMappingConvention([NotNull] IRelationalTypeMappingSource typeMappingSource)
{
_typeMappingSource = typeMappingSource;
}

/// <summary>
/// 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 InternalModelBuilder Apply(InternalModelBuilder modelBuilder)
{
foreach (var dbFunc in modelBuilder.Metadata.Relational().DbFunctions.Cast<IMutableDbFunction>())
{
var mutableDbFunc = modelBuilder.Relational(ConfigurationSource.Convention).GetOrAddDbFunction(dbFunc.MethodInfo);
mutableDbFunc.ReturnTypeMapping = _typeMappingSource.FindMapping(dbFunc.MethodInfo.ReturnType);

foreach (var dbFuncParam in dbFunc.Parameters.Cast<IMutableDbFunctionParameter>())
{
dbFuncParam.TypeMapping = _typeMappingSource.FindMapping(dbFuncParam.FunctionType);
}
}

return modelBuilder;
}
}
}
14 changes: 12 additions & 2 deletions src/EFCore.Relational/Metadata/IDbFunction.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using System.Collections.Generic;
using System.Linq.Expressions;
using System.Reflection;
using Microsoft.EntityFrameworkCore.Storage;

namespace Microsoft.EntityFrameworkCore.Metadata
{
Expand All @@ -13,8 +14,7 @@ namespace Microsoft.EntityFrameworkCore.Metadata
/// </summary>
public interface IDbFunction
{
/// <summary>
/// The name of the function in the database.
/// <summary>,abase.
/// </summary>
string FunctionName { get; }

Expand All @@ -32,5 +32,15 @@ public interface IDbFunction
/// A translation callback for performing custom translation of the method call into a SQL expression fragment.
/// </summary>
Func<IReadOnlyCollection<Expression>, Expression> Translation { get; }

/// <summary>
/// The type relation type mapping for the functions return type
/// </summary>
RelationalTypeMapping ReturnTypeMapping { get; }

/// <summary>
/// The parameters for this function
/// </summary>
IReadOnlyList<IDbFunctionParameter> Parameters { get; }
}
}
44 changes: 44 additions & 0 deletions src/EFCore.Relational/Metadata/IDbFunctionParameter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// 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 Microsoft.EntityFrameworkCore.Storage;

namespace Microsoft.EntityFrameworkCore.Metadata
{
/// <summary>
/// Represents a db function parametere in an <see cref="IDbFunction" />.
/// </summary>
public interface IDbFunctionParameter
{
/// <summary>
/// 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>
IDbFunction Parent { get; }

/// <summary>
/// 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>
string Name { get; }

/// <summary>
/// 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>
Type FunctionType { get; }

/// <summary>
/// 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>
bool SupportsNullabilityPropagation { get; }

/// <summary>
/// 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>
RelationalTypeMapping TypeMapping { get; }
}
}
6 changes: 6 additions & 0 deletions src/EFCore.Relational/Metadata/IMutableDbFunction.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using System.Collections.Generic;
using System.Linq.Expressions;
using JetBrains.Annotations;
using Microsoft.EntityFrameworkCore.Storage;

namespace Microsoft.EntityFrameworkCore.Metadata
{
Expand All @@ -24,6 +25,11 @@ public interface IMutableDbFunction : IDbFunction
/// </summary>
new string Schema { get; [param: CanBeNull] set; }

/// <summary>
/// The schema of the function in the database.
/// </summary>
new RelationalTypeMapping ReturnTypeMapping { get; set; }

/// <summary>
/// A translation callback for performing custom translation of the method call into a SQL expression fragment.
/// </summary>
Expand Down
34 changes: 34 additions & 0 deletions src/EFCore.Relational/Metadata/IMutableDbFunctionParameter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// 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.Collections.Generic;
using System.Text;
using Microsoft.EntityFrameworkCore.Storage;

namespace Microsoft.EntityFrameworkCore.Metadata
{
/// <summary>
/// Represents a db function parametere in an <see cref="IDbFunction" />.
/// </summary>
public interface IMutableDbFunctionParameter : IDbFunctionParameter
{
/// <summary>
/// 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>
new string Name { get; set; }

/// <summary>
/// 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>
new bool SupportsNullabilityPropagation { get; set; }

/// <summary>
/// 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>
new RelationalTypeMapping TypeMapping { get; set; }
}
}
25 changes: 23 additions & 2 deletions src/EFCore.Relational/Metadata/Internal/DbFunction.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
using Microsoft.EntityFrameworkCore.Internal;
using Microsoft.EntityFrameworkCore.Query.Expressions;
using Microsoft.EntityFrameworkCore.Query.ExpressionTranslators;
using Microsoft.EntityFrameworkCore.Storage;
using Microsoft.EntityFrameworkCore.Utilities;

namespace Microsoft.EntityFrameworkCore.Metadata.Internal
Expand Down Expand Up @@ -57,6 +58,8 @@ public static DbFunction FindDbFunction(
private ConfigurationSource? _schemaConfigurationSource;
private ConfigurationSource? _nameConfigurationSource;

private readonly List<DbFunctionParameter> _parameters;

private DbFunction(
[NotNull] MethodInfo methodInfo,
[NotNull] IMutableModel model,
Expand All @@ -75,7 +78,7 @@ private DbFunction(
&& !typeof(DbContext).IsAssignableFrom(methodInfo.DeclaringType))
{
throw new ArgumentException(
RelationalStrings.DbFunctionInvalidInstanceType(methodInfo.DisplayName(), methodInfo.DeclaringType.ShortDisplayName()));
RelationalStrings.DbFunctionInvalidInstanceType(methodInfo.DisplayName(), methodInfo.DeclaringType?.ShortDisplayName()));
}

if (methodInfo.ReturnType == null
Expand All @@ -88,6 +91,11 @@ private DbFunction(
MethodInfo = methodInfo;

_model = model;

_parameters = methodInfo.GetParameters()
.Select((pi, i) => new DbFunctionParameter(this, pi.Name, pi.ParameterType))
.ToList();

_model[BuildAnnotationName(annotationPrefix, methodInfo)] = this;
}

Expand All @@ -107,7 +115,7 @@ public static IEnumerable<IDbFunction> GetDbFunctions([NotNull] IModel model, [N
}

private static string BuildAnnotationName(string annotationPrefix, MethodBase methodBase)
=> $"{annotationPrefix}{methodBase.DeclaringType.ShortDisplayName()}{methodBase.Name}({string.Join(",", methodBase.GetParameters().Select(p => p.ParameterType.Name))})";
=> $"{annotationPrefix}{methodBase.DeclaringType?.ShortDisplayName()}{methodBase.Name}({string.Join(",", methodBase.GetParameters().Select(p => p.ParameterType.Name))})";

/// <summary>
/// This API supports the Entity Framework Core infrastructure and is not intended to be used
Expand Down Expand Up @@ -202,5 +210,18 @@ Expression IMethodCallTranslator.Translate(
return Translation?.Invoke(methodCallExpression.Arguments)
?? new SqlFunctionExpression(FunctionName, MethodInfo.ReturnType, Schema, methodCallExpression.Arguments);
}

/// <summary>
/// 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 IReadOnlyList<IDbFunctionParameter> Parameters => _parameters;


/// <summary>
/// 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 RelationalTypeMapping ReturnTypeMapping { get; set; }
}
}
Loading

0 comments on commit 52746ef

Please sign in to comment.