From 6829c342d0df1df45a259f2a3333827484312728 Mon Sep 17 00:00:00 2001 From: Koen Date: Sun, 26 Jul 2026 01:32:16 +0000 Subject: [PATCH] remove unused constraints --- .../ExpressiveQueryableEfCoreExtensions.cs | 54 +++++++------------ .../Infrastructure/AsyncQueryableTestBase.cs | 21 ++++++++ .../Tests/StubSignatureTests.cs | 31 +++++++++++ 3 files changed, 72 insertions(+), 34 deletions(-) create mode 100644 tests/ExpressiveSharp.EntityFrameworkCore.IntegrationTests/Tests/StubSignatureTests.cs diff --git a/src/ExpressiveSharp.EntityFrameworkCore/Extensions/ExpressiveQueryableEfCoreExtensions.cs b/src/ExpressiveSharp.EntityFrameworkCore/Extensions/ExpressiveQueryableEfCoreExtensions.cs index 01362723..938c689c 100644 --- a/src/ExpressiveSharp.EntityFrameworkCore/Extensions/ExpressiveQueryableEfCoreExtensions.cs +++ b/src/ExpressiveSharp.EntityFrameworkCore/Extensions/ExpressiveQueryableEfCoreExtensions.cs @@ -55,7 +55,6 @@ public static IExpressiveQueryable IgnoreQueryFilters( public static IExpressiveQueryable TagWith( this IExpressiveQueryable source, string tag) - where TEntity : class => EntityFrameworkQueryableExtensions.TagWith(source, tag).AsExpressive(); [EditorBrowsable(EditorBrowsableState.Never)] @@ -63,7 +62,6 @@ public static IExpressiveQueryable TagWithCallSite( this IExpressiveQueryable source, [System.Runtime.CompilerServices.CallerFilePath] string filePath = "", [System.Runtime.CompilerServices.CallerLineNumber] int lineNumber = 0) - where TEntity : class => EntityFrameworkQueryableExtensions.TagWithCallSite(source, filePath, lineNumber).AsExpressive(); // Include / ThenInclude run at runtime, not intercepted. @@ -108,7 +106,6 @@ public static Task AnyAsync( this IExpressiveQueryable source, Func predicate, CancellationToken cancellationToken = default) - where TEntity : class => throw new UnreachableException(InterceptedMessage); [PolyfillTarget(typeof(EntityFrameworkQueryableExtensions))] @@ -117,7 +114,6 @@ public static Task AllAsync( this IExpressiveQueryable source, Func predicate, CancellationToken cancellationToken = default) - where TEntity : class => throw new UnreachableException(InterceptedMessage); [PolyfillTarget(typeof(EntityFrameworkQueryableExtensions))] @@ -126,7 +122,6 @@ public static Task CountAsync( this IExpressiveQueryable source, Func predicate, CancellationToken cancellationToken = default) - where TEntity : class => throw new UnreachableException(InterceptedMessage); [PolyfillTarget(typeof(EntityFrameworkQueryableExtensions))] @@ -135,7 +130,6 @@ public static Task LongCountAsync( this IExpressiveQueryable source, Func predicate, CancellationToken cancellationToken = default) - where TEntity : class => throw new UnreachableException(InterceptedMessage); [PolyfillTarget(typeof(EntityFrameworkQueryableExtensions))] @@ -144,7 +138,6 @@ public static Task FirstAsync( this IExpressiveQueryable source, Func predicate, CancellationToken cancellationToken = default) - where TEntity : class => throw new UnreachableException(InterceptedMessage); [PolyfillTarget(typeof(EntityFrameworkQueryableExtensions))] @@ -153,7 +146,6 @@ public static Task FirstAsync( this IExpressiveQueryable source, Func predicate, CancellationToken cancellationToken = default) - where TEntity : class => throw new UnreachableException(InterceptedMessage); [PolyfillTarget(typeof(EntityFrameworkQueryableExtensions))] @@ -162,7 +154,6 @@ public static Task LastAsync( this IExpressiveQueryable source, Func predicate, CancellationToken cancellationToken = default) - where TEntity : class => throw new UnreachableException(InterceptedMessage); [PolyfillTarget(typeof(EntityFrameworkQueryableExtensions))] @@ -171,7 +162,6 @@ public static Task LastAsync( this IExpressiveQueryable source, Func predicate, CancellationToken cancellationToken = default) - where TEntity : class => throw new UnreachableException(InterceptedMessage); [PolyfillTarget(typeof(EntityFrameworkQueryableExtensions))] @@ -180,7 +170,6 @@ public static Task SingleAsync( this IExpressiveQueryable source, Func predicate, CancellationToken cancellationToken = default) - where TEntity : class => throw new UnreachableException(InterceptedMessage); [PolyfillTarget(typeof(EntityFrameworkQueryableExtensions))] @@ -189,88 +178,87 @@ public static Task SingleAsync( this IExpressiveQueryable source, Func predicate, CancellationToken cancellationToken = default) - where TEntity : class => throw new UnreachableException(InterceptedMessage); [PolyfillTarget(typeof(EntityFrameworkQueryableExtensions))] [EditorBrowsable(EditorBrowsableState.Never)] - public static Task SumAsync(this IExpressiveQueryable source, Func selector, CancellationToken cancellationToken = default) where TEntity : class => throw new UnreachableException(InterceptedMessage); + public static Task SumAsync(this IExpressiveQueryable source, Func selector, CancellationToken cancellationToken = default) => throw new UnreachableException(InterceptedMessage); [PolyfillTarget(typeof(EntityFrameworkQueryableExtensions))] [EditorBrowsable(EditorBrowsableState.Never)] - public static Task SumAsync(this IExpressiveQueryable source, Func selector, CancellationToken cancellationToken = default) where TEntity : class => throw new UnreachableException(InterceptedMessage); + public static Task SumAsync(this IExpressiveQueryable source, Func selector, CancellationToken cancellationToken = default) => throw new UnreachableException(InterceptedMessage); [PolyfillTarget(typeof(EntityFrameworkQueryableExtensions))] [EditorBrowsable(EditorBrowsableState.Never)] - public static Task SumAsync(this IExpressiveQueryable source, Func selector, CancellationToken cancellationToken = default) where TEntity : class => throw new UnreachableException(InterceptedMessage); + public static Task SumAsync(this IExpressiveQueryable source, Func selector, CancellationToken cancellationToken = default) => throw new UnreachableException(InterceptedMessage); [PolyfillTarget(typeof(EntityFrameworkQueryableExtensions))] [EditorBrowsable(EditorBrowsableState.Never)] - public static Task SumAsync(this IExpressiveQueryable source, Func selector, CancellationToken cancellationToken = default) where TEntity : class => throw new UnreachableException(InterceptedMessage); + public static Task SumAsync(this IExpressiveQueryable source, Func selector, CancellationToken cancellationToken = default) => throw new UnreachableException(InterceptedMessage); [PolyfillTarget(typeof(EntityFrameworkQueryableExtensions))] [EditorBrowsable(EditorBrowsableState.Never)] - public static Task SumAsync(this IExpressiveQueryable source, Func selector, CancellationToken cancellationToken = default) where TEntity : class => throw new UnreachableException(InterceptedMessage); + public static Task SumAsync(this IExpressiveQueryable source, Func selector, CancellationToken cancellationToken = default) => throw new UnreachableException(InterceptedMessage); [PolyfillTarget(typeof(EntityFrameworkQueryableExtensions))] [EditorBrowsable(EditorBrowsableState.Never)] - public static Task SumAsync(this IExpressiveQueryable source, Func selector, CancellationToken cancellationToken = default) where TEntity : class => throw new UnreachableException(InterceptedMessage); + public static Task SumAsync(this IExpressiveQueryable source, Func selector, CancellationToken cancellationToken = default) => throw new UnreachableException(InterceptedMessage); [PolyfillTarget(typeof(EntityFrameworkQueryableExtensions))] [EditorBrowsable(EditorBrowsableState.Never)] - public static Task SumAsync(this IExpressiveQueryable source, Func selector, CancellationToken cancellationToken = default) where TEntity : class => throw new UnreachableException(InterceptedMessage); + public static Task SumAsync(this IExpressiveQueryable source, Func selector, CancellationToken cancellationToken = default) => throw new UnreachableException(InterceptedMessage); [PolyfillTarget(typeof(EntityFrameworkQueryableExtensions))] [EditorBrowsable(EditorBrowsableState.Never)] - public static Task SumAsync(this IExpressiveQueryable source, Func selector, CancellationToken cancellationToken = default) where TEntity : class => throw new UnreachableException(InterceptedMessage); + public static Task SumAsync(this IExpressiveQueryable source, Func selector, CancellationToken cancellationToken = default) => throw new UnreachableException(InterceptedMessage); [PolyfillTarget(typeof(EntityFrameworkQueryableExtensions))] [EditorBrowsable(EditorBrowsableState.Never)] - public static Task SumAsync(this IExpressiveQueryable source, Func selector, CancellationToken cancellationToken = default) where TEntity : class => throw new UnreachableException(InterceptedMessage); + public static Task SumAsync(this IExpressiveQueryable source, Func selector, CancellationToken cancellationToken = default) => throw new UnreachableException(InterceptedMessage); [PolyfillTarget(typeof(EntityFrameworkQueryableExtensions))] [EditorBrowsable(EditorBrowsableState.Never)] - public static Task SumAsync(this IExpressiveQueryable source, Func selector, CancellationToken cancellationToken = default) where TEntity : class => throw new UnreachableException(InterceptedMessage); + public static Task SumAsync(this IExpressiveQueryable source, Func selector, CancellationToken cancellationToken = default) => throw new UnreachableException(InterceptedMessage); [PolyfillTarget(typeof(EntityFrameworkQueryableExtensions))] [EditorBrowsable(EditorBrowsableState.Never)] - public static Task AverageAsync(this IExpressiveQueryable source, Func selector, CancellationToken cancellationToken = default) where TEntity : class => throw new UnreachableException(InterceptedMessage); + public static Task AverageAsync(this IExpressiveQueryable source, Func selector, CancellationToken cancellationToken = default) => throw new UnreachableException(InterceptedMessage); [PolyfillTarget(typeof(EntityFrameworkQueryableExtensions))] [EditorBrowsable(EditorBrowsableState.Never)] - public static Task AverageAsync(this IExpressiveQueryable source, Func selector, CancellationToken cancellationToken = default) where TEntity : class => throw new UnreachableException(InterceptedMessage); + public static Task AverageAsync(this IExpressiveQueryable source, Func selector, CancellationToken cancellationToken = default) => throw new UnreachableException(InterceptedMessage); [PolyfillTarget(typeof(EntityFrameworkQueryableExtensions))] [EditorBrowsable(EditorBrowsableState.Never)] - public static Task AverageAsync(this IExpressiveQueryable source, Func selector, CancellationToken cancellationToken = default) where TEntity : class => throw new UnreachableException(InterceptedMessage); + public static Task AverageAsync(this IExpressiveQueryable source, Func selector, CancellationToken cancellationToken = default) => throw new UnreachableException(InterceptedMessage); [PolyfillTarget(typeof(EntityFrameworkQueryableExtensions))] [EditorBrowsable(EditorBrowsableState.Never)] - public static Task AverageAsync(this IExpressiveQueryable source, Func selector, CancellationToken cancellationToken = default) where TEntity : class => throw new UnreachableException(InterceptedMessage); + public static Task AverageAsync(this IExpressiveQueryable source, Func selector, CancellationToken cancellationToken = default) => throw new UnreachableException(InterceptedMessage); [PolyfillTarget(typeof(EntityFrameworkQueryableExtensions))] [EditorBrowsable(EditorBrowsableState.Never)] - public static Task AverageAsync(this IExpressiveQueryable source, Func selector, CancellationToken cancellationToken = default) where TEntity : class => throw new UnreachableException(InterceptedMessage); + public static Task AverageAsync(this IExpressiveQueryable source, Func selector, CancellationToken cancellationToken = default) => throw new UnreachableException(InterceptedMessage); [PolyfillTarget(typeof(EntityFrameworkQueryableExtensions))] [EditorBrowsable(EditorBrowsableState.Never)] - public static Task AverageAsync(this IExpressiveQueryable source, Func selector, CancellationToken cancellationToken = default) where TEntity : class => throw new UnreachableException(InterceptedMessage); + public static Task AverageAsync(this IExpressiveQueryable source, Func selector, CancellationToken cancellationToken = default) => throw new UnreachableException(InterceptedMessage); [PolyfillTarget(typeof(EntityFrameworkQueryableExtensions))] [EditorBrowsable(EditorBrowsableState.Never)] - public static Task AverageAsync(this IExpressiveQueryable source, Func selector, CancellationToken cancellationToken = default) where TEntity : class => throw new UnreachableException(InterceptedMessage); + public static Task AverageAsync(this IExpressiveQueryable source, Func selector, CancellationToken cancellationToken = default) => throw new UnreachableException(InterceptedMessage); [PolyfillTarget(typeof(EntityFrameworkQueryableExtensions))] [EditorBrowsable(EditorBrowsableState.Never)] - public static Task AverageAsync(this IExpressiveQueryable source, Func selector, CancellationToken cancellationToken = default) where TEntity : class => throw new UnreachableException(InterceptedMessage); + public static Task AverageAsync(this IExpressiveQueryable source, Func selector, CancellationToken cancellationToken = default) => throw new UnreachableException(InterceptedMessage); [PolyfillTarget(typeof(EntityFrameworkQueryableExtensions))] [EditorBrowsable(EditorBrowsableState.Never)] - public static Task AverageAsync(this IExpressiveQueryable source, Func selector, CancellationToken cancellationToken = default) where TEntity : class => throw new UnreachableException(InterceptedMessage); + public static Task AverageAsync(this IExpressiveQueryable source, Func selector, CancellationToken cancellationToken = default) => throw new UnreachableException(InterceptedMessage); [PolyfillTarget(typeof(EntityFrameworkQueryableExtensions))] [EditorBrowsable(EditorBrowsableState.Never)] - public static Task AverageAsync(this IExpressiveQueryable source, Func selector, CancellationToken cancellationToken = default) where TEntity : class => throw new UnreachableException(InterceptedMessage); + public static Task AverageAsync(this IExpressiveQueryable source, Func selector, CancellationToken cancellationToken = default) => throw new UnreachableException(InterceptedMessage); [PolyfillTarget(typeof(EntityFrameworkQueryableExtensions))] [EditorBrowsable(EditorBrowsableState.Never)] @@ -278,7 +266,6 @@ public static Task MinAsync( this IExpressiveQueryable source, Func selector, CancellationToken cancellationToken = default) - where TEntity : class => throw new UnreachableException(InterceptedMessage); [PolyfillTarget(typeof(EntityFrameworkQueryableExtensions))] @@ -287,6 +274,5 @@ public static Task MaxAsync( this IExpressiveQueryable source, Func selector, CancellationToken cancellationToken = default) - where TEntity : class => throw new UnreachableException(InterceptedMessage); } diff --git a/tests/ExpressiveSharp.EntityFrameworkCore.IntegrationTests/Infrastructure/AsyncQueryableTestBase.cs b/tests/ExpressiveSharp.EntityFrameworkCore.IntegrationTests/Infrastructure/AsyncQueryableTestBase.cs index 107890ae..58ffbff3 100644 --- a/tests/ExpressiveSharp.EntityFrameworkCore.IntegrationTests/Infrastructure/AsyncQueryableTestBase.cs +++ b/tests/ExpressiveSharp.EntityFrameworkCore.IntegrationTests/Infrastructure/AsyncQueryableTestBase.cs @@ -325,4 +325,25 @@ public async Task ElementAtOrDefaultAsync_OutOfRange_ReturnsNull() Assert.IsNull(result); } + + [TestMethod] + public async Task SumAsync_OverValueTypeProjection_WithModernSyntax_Executes() + { + var result = await Context.Orders.AsExpressiveDbSet() + .Select(o => o.Price) + .SumAsync(x => x switch { >= 50 => x, _ => 0.0 }); + + Assert.AreEqual(245.0, result); + } + + [TestMethod] + public async Task FirstAsync_OverValueTypeProjection_Executes() + { + var result = await Context.Orders.AsExpressiveDbSet() + .Select(o => o.Id) + .OrderBy(x => x) + .FirstAsync(x => x > 2); + + Assert.AreEqual(3, result); + } } diff --git a/tests/ExpressiveSharp.EntityFrameworkCore.IntegrationTests/Tests/StubSignatureTests.cs b/tests/ExpressiveSharp.EntityFrameworkCore.IntegrationTests/Tests/StubSignatureTests.cs new file mode 100644 index 00000000..b0e3c60f --- /dev/null +++ b/tests/ExpressiveSharp.EntityFrameworkCore.IntegrationTests/Tests/StubSignatureTests.cs @@ -0,0 +1,31 @@ +using System.Reflection; +using Microsoft.EntityFrameworkCore; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace ExpressiveSharp.EntityFrameworkCore.IntegrationTests.Tests; + +[TestClass] +public class StubSignatureTests +{ + [TestMethod] + public void Stubs_TEntityClassConstraint_MatchesEfCoreTargets() + { + var efNamesWithClassConstraint = typeof(EntityFrameworkQueryableExtensions) + .GetMethods(BindingFlags.Public | BindingFlags.Static) + .Where(m => m.IsGenericMethodDefinition) + .ToLookup(m => m.Name, HasClassConstraint); + + var overConstrained = typeof(ExpressiveQueryableEfCoreExtensions) + .GetMethods(BindingFlags.Public | BindingFlags.Static) + .Where(m => m.IsGenericMethodDefinition && efNamesWithClassConstraint.Contains(m.Name)) + .Where(m => HasClassConstraint(m) && !efNamesWithClassConstraint[m.Name].Any(c => c)) + .Select(m => m.ToString()) + .ToList(); + + Assert.AreEqual(0, overConstrained.Count); + } + + private static bool HasClassConstraint(MethodInfo method) => + method.GetGenericArguments()[0].GenericParameterAttributes + .HasFlag(GenericParameterAttributes.ReferenceTypeConstraint); +}