Skip to content

Commit

Permalink
Add Std Dev and Variance using EF.Functions (#269)
Browse files Browse the repository at this point in the history
* Add Std Dev and Variance using EF.Functions
  • Loading branch information
ChrisJollyAU authored Oct 13, 2024
1 parent 98bdd0a commit ec6c702
Show file tree
Hide file tree
Showing 8 changed files with 928 additions and 385 deletions.
369 changes: 334 additions & 35 deletions src/EFCore.Jet/Extensions/JetDbFunctionsExtensions.cs

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ public static IServiceCollection AddEntityFrameworkJet([NotNull] this IServiceCo
.TryAdd<ISingletonOptions, IJetOptions>(p => p.GetRequiredService<IJetOptions>())
.TryAdd<IQueryCompilationContextFactory, JetQueryCompilationContextFactory>()
.TryAdd<IMethodCallTranslatorProvider, JetMethodCallTranslatorProvider>()
.TryAdd<IAggregateMethodCallTranslatorProvider, JetAggregateMethodCallTranslatorProvider>()
.TryAdd<IMemberTranslatorProvider, JetMemberTranslatorProvider>()
.TryAdd<IQuerySqlGeneratorFactory, JetQuerySqlGeneratorFactory>()
.TryAdd<IRelationalSqlTranslatingExpressionVisitorFactory, JetSqlTranslatingExpressionVisitorFactory>()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
using Microsoft.EntityFrameworkCore.Query;

namespace EntityFrameworkCore.Jet.Query.ExpressionTranslators.Internal;

/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public class JetAggregateMethodCallTranslatorProvider : RelationalAggregateMethodCallTranslatorProvider
{
/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public JetAggregateMethodCallTranslatorProvider(RelationalAggregateMethodCallTranslatorProviderDependencies dependencies)
: base(dependencies)
{
var sqlExpressionFactory = dependencies.SqlExpressionFactory;
var typeMappingSource = dependencies.RelationalTypeMappingSource;

AddTranslators(
[
new JetStatisticsAggregateMethodTranslator(sqlExpressionFactory, typeMappingSource)
]);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ public class JetMethodCallTranslatorProvider : RelationalMethodCallTranslatorPro
/// directly from your code. This API may change or be removed in future releases.
/// </summary>
public JetMethodCallTranslatorProvider(
[NotNull] RelationalMethodCallTranslatorProviderDependencies dependencies)
RelationalMethodCallTranslatorProviderDependencies dependencies)
: base(dependencies)
{
var sqlExpressionFactory = (JetSqlExpressionFactory)dependencies.SqlExpressionFactory;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
using System.Collections.Generic;
using System.Reflection;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Diagnostics;
using Microsoft.EntityFrameworkCore.Query;
using Microsoft.EntityFrameworkCore.Query.SqlExpressions;
using Microsoft.EntityFrameworkCore.Storage;

namespace EntityFrameworkCore.Jet.Query.ExpressionTranslators.Internal;

/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public class JetStatisticsAggregateMethodTranslator : IAggregateMethodCallTranslator
{
private readonly ISqlExpressionFactory _sqlExpressionFactory;
private readonly RelationalTypeMapping _doubleTypeMapping;

/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public JetStatisticsAggregateMethodTranslator(
ISqlExpressionFactory sqlExpressionFactory,
IRelationalTypeMappingSource typeMappingSource)
{
_sqlExpressionFactory = sqlExpressionFactory;
_doubleTypeMapping = typeMappingSource.FindMapping(typeof(double))!;
}

/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public virtual SqlExpression? Translate(
MethodInfo method,
EnumerableExpression source,
IReadOnlyList<SqlExpression> arguments,
IDiagnosticsLogger<DbLoggerCategory.Query> logger)
{
// Docs: https://docs.microsoft.com/sql/t-sql/functions/aggregate-functions-transact-sql

if (method.DeclaringType != typeof(JetDbFunctionsExtensions)
|| source.Selector is not SqlExpression sqlExpression)
{
return null;
}

var functionName = method.Name switch
{
nameof(JetDbFunctionsExtensions.StandardDeviationSample) => "StDev",
nameof(JetDbFunctionsExtensions.StandardDeviationPopulation) => "StDevP",
nameof(JetDbFunctionsExtensions.VarianceSample) => "Var",
nameof(JetDbFunctionsExtensions.VariancePopulation) => "VarP",
_ => null
};

if (functionName is null)
{
return null;
}

return _sqlExpressionFactory.Function(functionName, [sqlExpression], nullable: true, argumentsPropagateNullability: [false], typeof(double), _doubleTypeMapping);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13060,6 +13060,8 @@ EntityFrameworkCore.Jet.FunctionalTests.Query.NorthwindFunctionsQueryJetTest.Sel
EntityFrameworkCore.Jet.FunctionalTests.Query.NorthwindFunctionsQueryJetTest.Select_mathf_truncate(async: True)
EntityFrameworkCore.Jet.FunctionalTests.Query.NorthwindFunctionsQueryJetTest.Select_ToString_IndexOf(async: False)
EntityFrameworkCore.Jet.FunctionalTests.Query.NorthwindFunctionsQueryJetTest.Select_ToString_IndexOf(async: True)
EntityFrameworkCore.Jet.FunctionalTests.Query.NorthwindFunctionsQueryJetTest.StandardDeviation(async: False)
EntityFrameworkCore.Jet.FunctionalTests.Query.NorthwindFunctionsQueryJetTest.StandardDeviation(async: True)
EntityFrameworkCore.Jet.FunctionalTests.Query.NorthwindFunctionsQueryJetTest.Static_equals_int_compared_to_long(isAsync: False)
EntityFrameworkCore.Jet.FunctionalTests.Query.NorthwindFunctionsQueryJetTest.Static_equals_int_compared_to_long(isAsync: True)
EntityFrameworkCore.Jet.FunctionalTests.Query.NorthwindFunctionsQueryJetTest.Static_equals_nullable_datetime_compared_to_non_nullable(isAsync: False)
Expand Down Expand Up @@ -13194,6 +13196,8 @@ EntityFrameworkCore.Jet.FunctionalTests.Query.NorthwindFunctionsQueryJetTest.Tri
EntityFrameworkCore.Jet.FunctionalTests.Query.NorthwindFunctionsQueryJetTest.TrimEnd_without_arguments_in_predicate(isAsync: True)
EntityFrameworkCore.Jet.FunctionalTests.Query.NorthwindFunctionsQueryJetTest.TrimStart_without_arguments_in_predicate(isAsync: False)
EntityFrameworkCore.Jet.FunctionalTests.Query.NorthwindFunctionsQueryJetTest.TrimStart_without_arguments_in_predicate(isAsync: True)
EntityFrameworkCore.Jet.FunctionalTests.Query.NorthwindFunctionsQueryJetTest.Variance(async: False)
EntityFrameworkCore.Jet.FunctionalTests.Query.NorthwindFunctionsQueryJetTest.Variance(async: True)
EntityFrameworkCore.Jet.FunctionalTests.Query.NorthwindFunctionsQueryJetTest.Where_DateOnly_FromDateTime(async: False)
EntityFrameworkCore.Jet.FunctionalTests.Query.NorthwindFunctionsQueryJetTest.Where_DateOnly_FromDateTime(async: True)
EntityFrameworkCore.Jet.FunctionalTests.Query.NorthwindFunctionsQueryJetTest.Where_functions_nested(isAsync: False)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14350,6 +14350,8 @@ EntityFrameworkCore.Jet.FunctionalTests.Query.NorthwindFunctionsQueryJetTest.Sel
EntityFrameworkCore.Jet.FunctionalTests.Query.NorthwindFunctionsQueryJetTest.Select_mathf_truncate(async: True)
EntityFrameworkCore.Jet.FunctionalTests.Query.NorthwindFunctionsQueryJetTest.Select_ToString_IndexOf(async: False)
EntityFrameworkCore.Jet.FunctionalTests.Query.NorthwindFunctionsQueryJetTest.Select_ToString_IndexOf(async: True)
EntityFrameworkCore.Jet.FunctionalTests.Query.NorthwindFunctionsQueryJetTest.StandardDeviation(async: False)
EntityFrameworkCore.Jet.FunctionalTests.Query.NorthwindFunctionsQueryJetTest.StandardDeviation(async: True)
EntityFrameworkCore.Jet.FunctionalTests.Query.NorthwindFunctionsQueryJetTest.Static_equals_int_compared_to_long(isAsync: False)
EntityFrameworkCore.Jet.FunctionalTests.Query.NorthwindFunctionsQueryJetTest.Static_equals_int_compared_to_long(isAsync: True)
EntityFrameworkCore.Jet.FunctionalTests.Query.NorthwindFunctionsQueryJetTest.Static_equals_nullable_datetime_compared_to_non_nullable(isAsync: False)
Expand Down Expand Up @@ -14484,6 +14486,8 @@ EntityFrameworkCore.Jet.FunctionalTests.Query.NorthwindFunctionsQueryJetTest.Tri
EntityFrameworkCore.Jet.FunctionalTests.Query.NorthwindFunctionsQueryJetTest.TrimEnd_without_arguments_in_predicate(isAsync: True)
EntityFrameworkCore.Jet.FunctionalTests.Query.NorthwindFunctionsQueryJetTest.TrimStart_without_arguments_in_predicate(isAsync: False)
EntityFrameworkCore.Jet.FunctionalTests.Query.NorthwindFunctionsQueryJetTest.TrimStart_without_arguments_in_predicate(isAsync: True)
EntityFrameworkCore.Jet.FunctionalTests.Query.NorthwindFunctionsQueryJetTest.Variance(async: False)
EntityFrameworkCore.Jet.FunctionalTests.Query.NorthwindFunctionsQueryJetTest.Variance(async: True)
EntityFrameworkCore.Jet.FunctionalTests.Query.NorthwindFunctionsQueryJetTest.Where_DateOnly_FromDateTime(async: False)
EntityFrameworkCore.Jet.FunctionalTests.Query.NorthwindFunctionsQueryJetTest.Where_DateOnly_FromDateTime(async: True)
EntityFrameworkCore.Jet.FunctionalTests.Query.NorthwindFunctionsQueryJetTest.Where_functions_nested(isAsync: False)
Expand Down
Loading

0 comments on commit ec6c702

Please sign in to comment.