Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Pregenerate SQL for precompiled queries #33510

Merged
merged 1 commit into from May 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 3 additions & 0 deletions EFCore.sln.DotSettings
Expand Up @@ -328,6 +328,9 @@ The .NET Foundation licenses this file to you under the MIT license.
<s:Boolean x:Key="/Default/UserDictionary/Words/=Postgre/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=precompilation/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=precompiling/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=pregenerate/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=pregenerated/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=pregeneration/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=prunable/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=precompilation/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=pubternal/@EntryIndexedValue">True</s:Boolean>
Expand Down
26 changes: 24 additions & 2 deletions src/EFCore.Design/Query/Internal/CSharpToLinqTranslator.cs
Expand Up @@ -226,6 +226,15 @@ public override Expression VisitBinaryExpression(BinaryExpressionSyntax binary)
var left = Visit(binary.Left);
var right = Visit(binary.Right);

if (Nullable.GetUnderlyingType(left.Type) == right.Type)
{
right = Convert(right, left.Type);
}
else if (Nullable.GetUnderlyingType(right.Type) == left.Type)
{
left = Convert(left, right.Type);
}

// https://learn.microsoft.com/dotnet/api/Microsoft.CodeAnalysis.CSharp.Syntax.BinaryExpressionSyntax
return binary.Kind() switch
{
Expand Down Expand Up @@ -406,7 +415,8 @@ public override Expression VisitIdentifierName(IdentifierNameSyntax identifierNa
new FakeFieldInfo(
typeof(FakeClosureFrameClass),
ResolveType(localSymbol.Type),
localSymbol.Name)));
localSymbol.Name,
localSymbol.NullableAnnotation is NullableAnnotation.NotAnnotated)));
}

throw new InvalidOperationException(
Expand Down Expand Up @@ -1123,6 +1133,11 @@ private Type ResolveType(ITypeSymbol typeSymbol, Dictionary<string, Type>? gener

Type GetClrType(INamedTypeSymbol symbol)
{
if (symbol.SpecialType == SpecialType.System_Nullable_T)
{
return typeof(Nullable<>);
}

var name = symbol.ContainingType is null
? typeSymbol.ToDisplayString(QualifiedTypeNameSymbolDisplayFormat)
: typeSymbol.Name;
Expand Down Expand Up @@ -1205,8 +1220,15 @@ public int GetHashCode(T[] obj)
[CompilerGenerated]
private sealed class FakeClosureFrameClass;

private sealed class FakeFieldInfo(Type declaringType, Type fieldType, string name) : FieldInfo
private sealed class FakeFieldInfo(
Type declaringType,
Type fieldType,
string name,
bool isNonNullableReferenceType)
: FieldInfo, IParameterNullabilityInfo
{
public bool IsNonNullableReferenceType { get; } = isNonNullableReferenceType;

public override object[] GetCustomAttributes(bool inherit)
=> Array.Empty<object>();

Expand Down
Expand Up @@ -851,7 +851,10 @@ void ProcessCapturedVariables()
.IncrementIndent()
.AppendLine("var relationalModel = dbContext.Model.GetRelationalModel();")
.AppendLine("var relationalTypeMappingSource = dbContext.GetService<IRelationalTypeMappingSource>();")
.AppendLine("var materializerLiftableConstantContext = new RelationalMaterializerLiftableConstantContext(dbContext.GetService<ShapedQueryCompilingExpressionVisitorDependencies>(), dbContext.GetService<RelationalShapedQueryCompilingExpressionVisitorDependencies>());");
.AppendLine("var materializerLiftableConstantContext = new RelationalMaterializerLiftableConstantContext(")
.AppendLine(" dbContext.GetService<ShapedQueryCompilingExpressionVisitorDependencies>(),")
cincuranet marked this conversation as resolved.
Show resolved Hide resolved
.AppendLine(" dbContext.GetService<RelationalShapedQueryCompilingExpressionVisitorDependencies>(),")
.AppendLine(" dbContext.GetService<RelationalCommandBuilderDependencies>());");

HashSet<string> variableNames = ["relationalModel", "relationalTypeMappingSource", "materializerLiftableConstantContext"];

Expand Down
Expand Up @@ -12,7 +12,7 @@ namespace Microsoft.EntityFrameworkCore.Internal;
/// 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 static class RelationCommandCacheExtensions
public static class RelationalCommandResolverExtensions
{
/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
Expand All @@ -21,10 +21,10 @@ public static class RelationCommandCacheExtensions
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public static IRelationalCommand RentAndPopulateRelationalCommand(
this RelationalCommandCache relationalCommandCache,
this RelationalCommandResolver relationalCommandResolver,
RelationalQueryContext queryContext)
{
var relationalCommandTemplate = relationalCommandCache.GetRelationalCommandTemplate(queryContext.ParameterValues);
var relationalCommandTemplate = relationalCommandResolver(queryContext.ParameterValues);
var relationalCommand = queryContext.Connection.RentCommand();
relationalCommand.PopulateFrom(relationalCommandTemplate);
return relationalCommand;
Expand Down
25 changes: 12 additions & 13 deletions src/EFCore.Relational/Query/Internal/FromSqlQueryingEnumerable.cs
Expand Up @@ -22,7 +22,7 @@ public static class FromSqlQueryingEnumerable
/// </summary>
public static FromSqlQueryingEnumerable<T> Create<T>(
RelationalQueryContext relationalQueryContext,
RelationalCommandCache relationalCommandCache,
RelationalCommandResolver relationalCommandResolver,
IReadOnlyList<ReaderColumn?>? readerColumns,
IReadOnlyList<string> columnNames,
Func<QueryContext, DbDataReader, int[], T> shaper,
Expand All @@ -32,7 +32,7 @@ public static class FromSqlQueryingEnumerable
bool threadSafetyChecksEnabled)
=> new(
relationalQueryContext,
relationalCommandCache,
relationalCommandResolver,
readerColumns,
columnNames,
shaper,
Expand All @@ -51,7 +51,7 @@ public static class FromSqlQueryingEnumerable
public class FromSqlQueryingEnumerable<T> : IEnumerable<T>, IAsyncEnumerable<T>, IRelationalQueryingEnumerable
{
private readonly RelationalQueryContext _relationalQueryContext;
private readonly RelationalCommandCache _relationalCommandCache;
private readonly RelationalCommandResolver _relationalCommandResolver;
private readonly IReadOnlyList<ReaderColumn?>? _readerColumns;
private readonly IReadOnlyList<string> _columnNames;
private readonly Func<QueryContext, DbDataReader, int[], T> _shaper;
Expand All @@ -69,7 +69,7 @@ public class FromSqlQueryingEnumerable<T> : IEnumerable<T>, IAsyncEnumerable<T>,
/// </summary>
public FromSqlQueryingEnumerable(
RelationalQueryContext relationalQueryContext,
RelationalCommandCache relationalCommandCache,
RelationalCommandResolver relationalCommandResolver,
IReadOnlyList<ReaderColumn?>? readerColumns,
IReadOnlyList<string> columnNames,
Func<QueryContext, DbDataReader, int[], T> shaper,
Expand All @@ -79,7 +79,7 @@ public class FromSqlQueryingEnumerable<T> : IEnumerable<T>, IAsyncEnumerable<T>,
bool threadSafetyChecksEnabled)
{
_relationalQueryContext = relationalQueryContext;
_relationalCommandCache = relationalCommandCache;
_relationalCommandResolver = relationalCommandResolver;
_readerColumns = readerColumns;
_columnNames = columnNames;
_shaper = shaper;
Expand Down Expand Up @@ -128,8 +128,7 @@ IEnumerator IEnumerable.GetEnumerator()
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public virtual DbCommand CreateDbCommand()
=> _relationalCommandCache
.GetRelationalCommandTemplate(_relationalQueryContext.ParameterValues)
=> _relationalCommandResolver(_relationalQueryContext.ParameterValues)
.CreateDbCommand(
new RelationalCommandParameterObject(
_relationalQueryContext.Connection,
Expand Down Expand Up @@ -187,7 +186,7 @@ public static int[] BuildIndexMap(IReadOnlyList<string> columnNames, DbDataReade
private sealed class Enumerator : IEnumerator<T>
{
private readonly RelationalQueryContext _relationalQueryContext;
private readonly RelationalCommandCache _relationalCommandCache;
private readonly RelationalCommandResolver _relationalCommandResolver;
private readonly IReadOnlyList<ReaderColumn?>? _readerColumns;
private readonly IReadOnlyList<string> _columnNames;
private readonly Func<QueryContext, DbDataReader, int[], T> _shaper;
Expand All @@ -205,7 +204,7 @@ private sealed class Enumerator : IEnumerator<T>
public Enumerator(FromSqlQueryingEnumerable<T> queryingEnumerable)
{
_relationalQueryContext = queryingEnumerable._relationalQueryContext;
_relationalCommandCache = queryingEnumerable._relationalCommandCache;
_relationalCommandResolver = queryingEnumerable._relationalCommandResolver;
_readerColumns = queryingEnumerable._readerColumns;
_columnNames = queryingEnumerable._columnNames;
_shaper = queryingEnumerable._shaper;
Expand Down Expand Up @@ -272,7 +271,7 @@ private static bool InitializeReader(Enumerator enumerator)
EntityFrameworkEventSource.Log.QueryExecuting();

var relationalCommand = enumerator._relationalCommand =
enumerator._relationalCommandCache.RentAndPopulateRelationalCommand(enumerator._relationalQueryContext);
enumerator._relationalCommandResolver.RentAndPopulateRelationalCommand(enumerator._relationalQueryContext);

enumerator._dataReader = relationalCommand.ExecuteReader(
new RelationalCommandParameterObject(
Expand Down Expand Up @@ -307,7 +306,7 @@ public void Reset()
private sealed class AsyncEnumerator : IAsyncEnumerator<T>
{
private readonly RelationalQueryContext _relationalQueryContext;
private readonly RelationalCommandCache _relationalCommandCache;
private readonly RelationalCommandResolver _relationalCommandResolver;
private readonly IReadOnlyList<ReaderColumn?>? _readerColumns;
private readonly IReadOnlyList<string> _columnNames;
private readonly Func<QueryContext, DbDataReader, int[], T> _shaper;
Expand All @@ -325,7 +324,7 @@ private sealed class AsyncEnumerator : IAsyncEnumerator<T>
public AsyncEnumerator(FromSqlQueryingEnumerable<T> queryingEnumerable)
{
_relationalQueryContext = queryingEnumerable._relationalQueryContext;
_relationalCommandCache = queryingEnumerable._relationalCommandCache;
_relationalCommandResolver = queryingEnumerable._relationalCommandResolver;
_readerColumns = queryingEnumerable._readerColumns;
_columnNames = queryingEnumerable._columnNames;
_shaper = queryingEnumerable._shaper;
Expand Down Expand Up @@ -394,7 +393,7 @@ private static async Task<bool> InitializeReaderAsync(AsyncEnumerator enumerator
EntityFrameworkEventSource.Log.QueryExecuting();

var relationalCommand = enumerator._relationalCommand =
enumerator._relationalCommandCache.RentAndPopulateRelationalCommand(enumerator._relationalQueryContext);
enumerator._relationalCommandResolver.RentAndPopulateRelationalCommand(enumerator._relationalQueryContext);

enumerator._dataReader = await relationalCommand.ExecuteReaderAsync(
new RelationalCommandParameterObject(
Expand Down
Expand Up @@ -22,7 +22,7 @@ public static class GroupBySingleQueryingEnumerable
/// </summary>
public static GroupBySingleQueryingEnumerable<TKey, TElement> Create<TKey, TElement>(
RelationalQueryContext relationalQueryContext,
RelationalCommandCache relationalCommandCache,
RelationalCommandResolver relationalCommandResolver,
IReadOnlyList<ReaderColumn?>? readerColumns,
Func<QueryContext, DbDataReader, TKey> keySelector,
Func<QueryContext, DbDataReader, object[]> keyIdentifier,
Expand All @@ -34,7 +34,7 @@ public static class GroupBySingleQueryingEnumerable
bool threadSafetyChecksEnabled)
=> new(
relationalQueryContext,
relationalCommandCache,
relationalCommandResolver,
readerColumns,
keySelector,
keyIdentifier,
Expand All @@ -56,7 +56,7 @@ public class GroupBySingleQueryingEnumerable<TKey, TElement>
: IEnumerable<IGrouping<TKey, TElement>>, IAsyncEnumerable<IGrouping<TKey, TElement>>, IRelationalQueryingEnumerable
{
private readonly RelationalQueryContext _relationalQueryContext;
private readonly RelationalCommandCache _relationalCommandCache;
private readonly RelationalCommandResolver _relationalCommandResolver;
private readonly IReadOnlyList<ReaderColumn?>? _readerColumns;
private readonly Func<QueryContext, DbDataReader, TKey> _keySelector;
private readonly Func<QueryContext, DbDataReader, object[]> _keyIdentifier;
Expand All @@ -76,7 +76,7 @@ public class GroupBySingleQueryingEnumerable<TKey, TElement>
/// </summary>
public GroupBySingleQueryingEnumerable(
RelationalQueryContext relationalQueryContext,
RelationalCommandCache relationalCommandCache,
RelationalCommandResolver relationalCommandResolver,
IReadOnlyList<ReaderColumn?>? readerColumns,
Func<QueryContext, DbDataReader, TKey> keySelector,
Func<QueryContext, DbDataReader, object[]> keyIdentifier,
Expand All @@ -88,7 +88,7 @@ public class GroupBySingleQueryingEnumerable<TKey, TElement>
bool threadSafetyChecksEnabled)
{
_relationalQueryContext = relationalQueryContext;
_relationalCommandCache = relationalCommandCache;
_relationalCommandResolver = relationalCommandResolver;
_readerColumns = readerColumns;
_keySelector = keySelector;
_keyIdentifier = keyIdentifier;
Expand Down Expand Up @@ -139,8 +139,7 @@ IEnumerator IEnumerable.GetEnumerator()
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public virtual DbCommand CreateDbCommand()
=> _relationalCommandCache
.GetRelationalCommandTemplate(_relationalQueryContext.ParameterValues)
=> _relationalCommandResolver(_relationalQueryContext.ParameterValues)
.CreateDbCommand(
new RelationalCommandParameterObject(
_relationalQueryContext.Connection,
Expand Down Expand Up @@ -196,7 +195,7 @@ private static bool CompareIdentifiers(IReadOnlyList<Func<object, object, bool>>
private sealed class Enumerator : IEnumerator<IGrouping<TKey, TElement>>
{
private readonly RelationalQueryContext _relationalQueryContext;
private readonly RelationalCommandCache _relationalCommandCache;
private readonly RelationalCommandResolver _relationalCommandResolver;
private readonly IReadOnlyList<ReaderColumn?>? _readerColumns;
private readonly Func<QueryContext, DbDataReader, TKey> _keySelector;
private readonly Func<QueryContext, DbDataReader, object[]> _keyIdentifier;
Expand All @@ -217,7 +216,7 @@ private sealed class Enumerator : IEnumerator<IGrouping<TKey, TElement>>
public Enumerator(GroupBySingleQueryingEnumerable<TKey, TElement> queryingEnumerable)
{
_relationalQueryContext = queryingEnumerable._relationalQueryContext;
_relationalCommandCache = queryingEnumerable._relationalCommandCache;
_relationalCommandResolver = queryingEnumerable._relationalCommandResolver;
_readerColumns = queryingEnumerable._readerColumns;
_keySelector = queryingEnumerable._keySelector;
_keyIdentifier = queryingEnumerable._keyIdentifier;
Expand Down Expand Up @@ -342,7 +341,7 @@ private static bool InitializeReader(Enumerator enumerator)
EntityFrameworkEventSource.Log.QueryExecuting();

var relationalCommand = enumerator._relationalCommand =
enumerator._relationalCommandCache.RentAndPopulateRelationalCommand(enumerator._relationalQueryContext);
enumerator._relationalCommandResolver.RentAndPopulateRelationalCommand(enumerator._relationalQueryContext);

var dataReader = enumerator._dataReader = relationalCommand.ExecuteReader(
new RelationalCommandParameterObject(
Expand Down Expand Up @@ -380,7 +379,7 @@ public void Reset()
private sealed class AsyncEnumerator : IAsyncEnumerator<IGrouping<TKey, TElement>>
{
private readonly RelationalQueryContext _relationalQueryContext;
private readonly RelationalCommandCache _relationalCommandCache;
private readonly RelationalCommandResolver _relationalCommandResolver;
private readonly IReadOnlyList<ReaderColumn?>? _readerColumns;
private readonly Func<QueryContext, DbDataReader, TKey> _keySelector;
private readonly Func<QueryContext, DbDataReader, object[]> _keyIdentifier;
Expand All @@ -402,7 +401,7 @@ private sealed class AsyncEnumerator : IAsyncEnumerator<IGrouping<TKey, TElement
public AsyncEnumerator(GroupBySingleQueryingEnumerable<TKey, TElement> queryingEnumerable)
{
_relationalQueryContext = queryingEnumerable._relationalQueryContext;
_relationalCommandCache = queryingEnumerable._relationalCommandCache;
_relationalCommandResolver = queryingEnumerable._relationalCommandResolver;
_readerColumns = queryingEnumerable._readerColumns;
_keySelector = queryingEnumerable._keySelector;
_keyIdentifier = queryingEnumerable._keyIdentifier;
Expand Down Expand Up @@ -529,7 +528,7 @@ private static async Task<bool> InitializeReaderAsync(AsyncEnumerator enumerator
EntityFrameworkEventSource.Log.QueryExecuting();

var relationalCommand = enumerator._relationalCommand =
enumerator._relationalCommandCache.RentAndPopulateRelationalCommand(enumerator._relationalQueryContext);
enumerator._relationalCommandResolver.RentAndPopulateRelationalCommand(enumerator._relationalQueryContext);

var dataReader = enumerator._dataReader = await relationalCommand.ExecuteReaderAsync(
new RelationalCommandParameterObject(
Expand Down