Skip to content

Commit

Permalink
Introduce liftable constants to shaper to prepare for precompilation
Browse files Browse the repository at this point in the history
Part of #25009
  • Loading branch information
roji authored and maumar committed Mar 26, 2024
1 parent f99afbb commit 40b6fd2
Show file tree
Hide file tree
Showing 175 changed files with 4,329 additions and 1,338 deletions.
1 change: 1 addition & 0 deletions EFCore.sln.DotSettings
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,7 @@ The .NET Foundation licenses this file to you under the MIT license.
<s:Boolean x:Key="/Default/UserDictionary/Words/=Includable/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=initializers/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=keyless/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=liftable/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Lite_0027s/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=materializer/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=materializers/@EntryIndexedValue">True</s:Boolean>
Expand Down
14 changes: 14 additions & 0 deletions src/EFCore.Analyzers/EFDiagnostics.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

namespace Microsoft.EntityFrameworkCore;

/// <summary>
/// Contains IDs of diagnostics created by EF analyzers and other mechanisms.
/// </summary>
public static class EFDiagnostics
{
public const string InternalUsage = "EF1001";
public const string SuppressUninitializedDbSetRule = "EFSPR1001";
public const string PrecompiledQueryExperimental = "EF2001";
}
41 changes: 41 additions & 0 deletions src/EFCore.Cosmos/Query/CosmosLiftableConstantFactory.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using Microsoft.EntityFrameworkCore.Cosmos.Query.Internal;
using Microsoft.EntityFrameworkCore.Query.Internal;

namespace Microsoft.EntityFrameworkCore.Cosmos.Query;

/// <summary>
/// TODO
/// </summary>
public class CosmosLiftableConstantFactory : LiftableConstantFactory, ICosmosLiftableConstantFactory
{
/// <summary>
/// TODO
/// </summary>
public CosmosLiftableConstantFactory(
#pragma warning disable EF1001 // Internal EF Core API usage.
LiftableConstantExpressionDependencies dependencies)
#pragma warning restore EF1001 // Internal EF Core API usage.
// CosmosLiftableConstantExpressionDependencies cosmosDependencies)
: base(dependencies)
{
// CosmosDependencies = cosmosDependencies;
}

///// <summary>
///// TODO
///// </summary>
//public virtual CosmosLiftableConstantExpressionDependencies CosmosDependencies { get; }

/// <summary>
/// TODO
/// </summary>
public virtual LiftableConstantExpression CreateLiftableConstant(
ConstantExpression originalExpression,
Expression<Func<CosmosMaterializerLiftableConstantContext, object>> resolverExpression,
string variableName,
Type type)
=> new(originalExpression, resolverExpression, variableName, type);
}
42 changes: 42 additions & 0 deletions src/EFCore.Cosmos/Query/CosmosLiftableConstantProcessor.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using Microsoft.EntityFrameworkCore.Cosmos.Query.Internal;
using Microsoft.EntityFrameworkCore.Query.Internal;

namespace Microsoft.EntityFrameworkCore.Cosmos.Query;

#pragma warning disable EF1001 // LiftableConstantProcessor is internal

/// <summary>
/// TODO
/// </summary>
public class CosmosLiftableConstantProcessor : LiftableConstantProcessor
{
private readonly CosmosMaterializerLiftableConstantContext _cosmosMaterializerLiftableConstantContext;

/// <summary>
/// TODO
/// </summary>
public CosmosLiftableConstantProcessor(
ShapedQueryCompilingExpressionVisitorDependencies dependencies,
IQuerySqlGeneratorFactory querySqlGeneratorFactory,
ISqlExpressionFactory sqlExpressionFactory)
//CosmosShapedQueryCompilingExpressionVisitorDependencies cosmosDependencies)
: base(dependencies)
=> _cosmosMaterializerLiftableConstantContext = new(dependencies, querySqlGeneratorFactory, sqlExpressionFactory);

/// <inheritdoc/>
protected override ConstantExpression InlineConstant(LiftableConstantExpression liftableConstant)
{
if (liftableConstant.ResolverExpression is Expression<Func<CosmosMaterializerLiftableConstantContext, object>>
resolverExpression)
{
var resolver = resolverExpression.Compile(preferInterpretation: true);
var value = resolver(_cosmosMaterializerLiftableConstantContext);
return Expression.Constant(value, liftableConstant.Type);
}

return base.InlineConstant(liftableConstant);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using Microsoft.EntityFrameworkCore.Cosmos.Query.Internal;

namespace Microsoft.EntityFrameworkCore.Cosmos.Query;

/// <summary>
/// TODO
/// </summary>
public sealed record CosmosMaterializerLiftableConstantContext(
ShapedQueryCompilingExpressionVisitorDependencies Dependencies,
IQuerySqlGeneratorFactory QuerySqlGeneratorFactory,
ISqlExpressionFactory SqlExpressionFactory)
//CosmosShapedQueryCompilingExpressionVisitorDependencies CosmosDependencies)
: MaterializerLiftableConstantContext(Dependencies);
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using Microsoft.EntityFrameworkCore.Cosmos.Query.Internal;

namespace Microsoft.EntityFrameworkCore.Cosmos.Query;

/// <summary>
/// <para>
/// Service dependencies parameter class for <see cref="CosmosShapedQueryCompilingExpressionVisitorFactory" />
/// </para>
/// <para>
/// This type is typically used by database providers (and other extensions). It is generally
/// not used in application code.
/// </para>
/// </summary>
/// <remarks>
/// <para>
/// Do not construct instances of this class directly from either provider or application code as the
/// constructor signature may change as new dependencies are added. Instead, use this type in
/// your constructor so that an instance will be created and injected automatically by the
/// dependency injection container. To create an instance with some dependent services replaced,
/// first resolve the object from the dependency injection container, then replace selected
/// services using the C# 'with' operator. Do not call the constructor at any point in this process.
/// </para>
/// <para>
/// The service lifetime is <see cref="ServiceLifetime.Scoped" />. This means that each
/// <see cref="DbContext" /> instance will use its own instance of this service.
/// The implementation may depend on other services registered with any lifetime.
/// The implementation does not need to be thread-safe.
/// </para>
/// </remarks>
public sealed record CosmosShapedQueryCompilingExpressionVisitorDependencies
{
/// <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>
/// <remarks>
/// Do not call this constructor directly from either provider or application code as it may change
/// as new dependencies are added. Instead, use this type in your constructor so that an instance
/// will be created and injected automatically by the dependency injection container. To create
/// an instance with some dependent services replaced, first resolve the object from the dependency
/// injection container, then replace selected services using the C# 'with' operator. Do not call
/// the constructor at any point in this process.
/// </remarks>
[EntityFrameworkInternal]
public CosmosShapedQueryCompilingExpressionVisitorDependencies(
IQuerySqlGeneratorFactory querySqlGeneratorFactory,
ISqlExpressionFactory sqlExpressionFactory,
//IRelationalParameterBasedSqlProcessorFactory relationalParameterBasedSqlProcessorFactory,
ICosmosLiftableConstantFactory cosmosLiftableConstantFactory)
{
QuerySqlGeneratorFactory = querySqlGeneratorFactory;
SqlExpressionFactory = sqlExpressionFactory;
//RelationalParameterBasedSqlProcessorFactory = relationalParameterBasedSqlProcessorFactory;
CosmosLiftableConstantFactory = cosmosLiftableConstantFactory;
}

/// <summary>
/// The SQL generator factory.
/// </summary>
public IQuerySqlGeneratorFactory QuerySqlGeneratorFactory { get; init; }

/// <summary>
/// The SQL expression factory.
/// </summary>
public ISqlExpressionFactory SqlExpressionFactory { get; init; }

///// <summary>
///// The SQL processor based on parameter values.
///// </summary>
//public IRelationalParameterBasedSqlProcessorFactory RelationalParameterBasedSqlProcessorFactory { get; init; }

/// <summary>
/// The liftable constant factory.
/// </summary>
public ICosmosLiftableConstantFactory CosmosLiftableConstantFactory { get; init; }
}
19 changes: 19 additions & 0 deletions src/EFCore.Cosmos/Query/ICosmosLiftableConstantFactory.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

namespace Microsoft.EntityFrameworkCore.Cosmos.Query;

/// <summary>
/// TODO
/// </summary>
public interface ICosmosLiftableConstantFactory : ILiftableConstantFactory
{
/// <summary>
/// TODO
/// </summary>
LiftableConstantExpression CreateLiftableConstant(
ConstantExpression originalExpression,
Expression<Func<CosmosMaterializerLiftableConstantContext, object>> resolverExpression,
string variableName,
Type type);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

namespace Microsoft.EntityFrameworkCore.Cosmos.Query.Internal;

/// <summary>
/// <para>
/// Service dependencies parameter class for <see cref="CosmosLiftableConstantFactory" />
/// </para>
/// <para>
/// This type is typically used by database providers (and other extensions). It is generally
/// not used in application code.
/// </para>
/// </summary>
/// <remarks>
/// <para>
/// Do not construct instances of this class directly from either provider or application code as the
/// constructor signature may change as new dependencies are added. Instead, use this type in
/// your constructor so that an instance will be created and injected automatically by the
/// dependency injection container. To create an instance with some dependent services replaced,
/// first resolve the object from the dependency injection container, then replace selected
/// services using the C# 'with' operator. Do not call the constructor at any point in this process.
/// </para>
/// <para>
/// The service lifetime is <see cref="ServiceLifetime.Singleton" />. This means a single instance
/// is used by many <see cref="DbContext" /> instances. The implementation must be thread-safe.
/// This service cannot depend on services registered as <see cref="ServiceLifetime.Scoped" />.
/// </para>
/// </remarks>
public sealed record CosmosLiftableConstantExpressionDependencies
{
}
Original file line number Diff line number Diff line change
Expand Up @@ -341,6 +341,10 @@ static bool IsTypeConstant(Expression expression, out Type? type)
}
}

//private static readonly MethodInfo PropertyGetValueConverterMethod
// = typeof(IReadOnlyProperty).GetMethod(nameof(IReadOnlyProperty.GetValueComparer))!;
// //= typeof(IProperty).GetMethod(nameof(IProperty.GetValueComparer))!;

private static bool TryUseComparer(
Expression? newLeft,
Expression? newRight,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
// The .NET Foundation licenses this file to you under the MIT license.

using Microsoft.EntityFrameworkCore.InMemory.Internal;
using Microsoft.EntityFrameworkCore.Internal;
using ExpressionExtensions = Microsoft.EntityFrameworkCore.Infrastructure.ExpressionExtensions;

namespace Microsoft.EntityFrameworkCore.InMemory.Query.Internal;
Expand Down Expand Up @@ -896,6 +895,9 @@ private static Expression GetGroupingKey(Expression key, List<Expression> groupi
}
}

private static readonly MethodInfo PropertyGetValueConverterMethod
= typeof(IReadOnlyProperty).GetMethod(nameof(IReadOnlyProperty.GetValueComparer))!;

private Expression AddJoin(
InMemoryQueryExpression innerQueryExpression,
LambdaExpression? outerKeySelector,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ public virtual InstantiationBinding ModifyBinding(InstantiationBindingIntercepti

return new FactoryMethodBinding(
_proxyFactory,
Expression.Constant(_proxyFactory, typeof(IProxyFactory)),
CreateLazyLoadingProxyMethod,
new List<ParameterBinding>
{
Expand All @@ -67,6 +68,7 @@ public virtual InstantiationBinding ModifyBinding(InstantiationBindingIntercepti
{
return new FactoryMethodBinding(
_proxyFactory,
Expression.Constant(_proxyFactory, typeof(IProxyFactory)),
CreateProxyMethod,
new List<ParameterBinding>
{
Expand Down
2 changes: 1 addition & 1 deletion src/EFCore.Relational/EFCore.Relational.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
<RootNamespace>Microsoft.EntityFrameworkCore</RootNamespace>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<ImplicitUsings>true</ImplicitUsings>
<NoWarn>$(NoWarn);EF1003</NoWarn> <!-- Precomiled query is experimental -->
<NoWarn>$(NoWarn);EF2001</NoWarn> <!-- Precomiled query is experimental -->
</PropertyGroup>

<ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ public class EntityFrameworkRelationalServicesBuilder : EntityFrameworkServicesB
{ typeof(IQuerySqlGeneratorFactory), new ServiceCharacteristics(ServiceLifetime.Singleton) },
{ typeof(IModificationCommandFactory), new ServiceCharacteristics(ServiceLifetime.Singleton) },
{ typeof(ISqlAliasManagerFactory), new ServiceCharacteristics(ServiceLifetime.Singleton) },
{ typeof(IRelationalLiftableConstantFactory), new ServiceCharacteristics(ServiceLifetime.Singleton) },
{ typeof(ICommandBatchPreparer), new ServiceCharacteristics(ServiceLifetime.Scoped) },
{ typeof(IModificationCommandBatchFactory), new ServiceCharacteristics(ServiceLifetime.Scoped) },
{ typeof(IRelationalSqlTranslatingExpressionVisitorFactory), new ServiceCharacteristics(ServiceLifetime.Scoped) },
Expand Down Expand Up @@ -189,6 +190,9 @@ public override EntityFrameworkServicesBuilder TryAddCoreServices()
TryAdd<IQueryCompilationContextFactory, RelationalQueryCompilationContextFactory>();
TryAdd<IAdHocMapper, RelationalAdHocMapper>();
TryAdd<ISqlAliasManagerFactory, SqlAliasManagerFactory>();
TryAdd<ILiftableConstantFactory>(p => p.GetRequiredService<IRelationalLiftableConstantFactory>());
TryAdd<IRelationalLiftableConstantFactory, RelationalLiftableConstantFactory>();
TryAdd<ILiftableConstantProcessor, RelationalLiftableConstantProcessor>();

ServiceCollectionMap.GetInfrastructure()
.AddDependencySingleton<RelationalSqlGenerationHelperDependencies>()
Expand All @@ -204,6 +208,7 @@ public override EntityFrameworkServicesBuilder TryAddCoreServices()
.AddDependencySingleton<RelationalEvaluatableExpressionFilterDependencies>()
.AddDependencySingleton<RelationalModelDependencies>()
.AddDependencySingleton<RelationalModelRuntimeInitializerDependencies>()
.AddDependencySingleton<RelationalLiftableConstantExpressionDependencies>()
.AddDependencyScoped<MigrationsSqlGeneratorDependencies>()
.AddDependencyScoped<RelationalConventionSetBuilderDependencies>()
.AddDependencyScoped<ModificationCommandBatchFactoryDependencies>()
Expand Down
19 changes: 19 additions & 0 deletions src/EFCore.Relational/Query/IRelationalLiftableConstantFactory.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

namespace Microsoft.EntityFrameworkCore.Query;

/// <summary>
/// TODO
/// </summary>
public interface IRelationalLiftableConstantFactory : ILiftableConstantFactory
{
/// <summary>
/// TODO
/// </summary>
LiftableConstantExpression CreateLiftableConstant(
ConstantExpression originalExpression,
Expression<Func<RelationalMaterializerLiftableConstantContext, object>> resolverExpression,
string variableName,
Type type);
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ namespace Microsoft.EntityFrameworkCore.Query;
/// Represents an expression that is quotable, that is, capable of returning an expression that, when evaluated, would construct an
/// expression identical to this one. Used to generate code for precompiled queries, which reconstructs this expression.
/// </summary>
[Experimental("EF1003")]
[Experimental(EFDiagnostics.PrecompiledQueryExperimental)]
public interface IRelationalQuotableExpression
{
/// <summary>
Expand Down

0 comments on commit 40b6fd2

Please sign in to comment.