From b758c5d3216f07f10877317f0350a7bd0f42c61c Mon Sep 17 00:00:00 2001 From: Smit Patel Date: Mon, 10 Jun 2019 16:10:27 -0700 Subject: [PATCH] Query: Combine QueryingEnumerable for regular and non composed FromSql case --- .../Query/Pipeline/AsyncQueryingEnumerable.cs | 51 ++++- ...omSqlNonComposedAsyncQueryingEnumerable.cs | 181 ------------------ .../FromSqlNonComposedQueryingEnumerable.cs | 177 ----------------- .../Query/Pipeline/QueryingEnumerable.cs | 51 ++++- ...alShapedQueryCompilingExpressionVisitor.cs | 23 +-- 5 files changed, 96 insertions(+), 387 deletions(-) delete mode 100644 src/EFCore.Relational/Query/Pipeline/FromSqlNonComposedAsyncQueryingEnumerable.cs delete mode 100644 src/EFCore.Relational/Query/Pipeline/FromSqlNonComposedQueryingEnumerable.cs diff --git a/src/EFCore.Relational/Query/Pipeline/AsyncQueryingEnumerable.cs b/src/EFCore.Relational/Query/Pipeline/AsyncQueryingEnumerable.cs index df71182ae00..a1203e86c4e 100644 --- a/src/EFCore.Relational/Query/Pipeline/AsyncQueryingEnumerable.cs +++ b/src/EFCore.Relational/Query/Pipeline/AsyncQueryingEnumerable.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Data.Common; +using System.Linq; using System.Threading; using System.Threading.Tasks; using Microsoft.EntityFrameworkCore.Diagnostics; @@ -19,7 +20,7 @@ private class AsyncQueryingEnumerable : IAsyncEnumerable { private readonly RelationalQueryContext _relationalQueryContext; private readonly SelectExpression _selectExpression; - private readonly Func> _shaper; + private readonly Func> _shaper; private readonly IQuerySqlGeneratorFactory _querySqlGeneratorFactory; private readonly Type _contextType; private readonly IDiagnosticsLogger _logger; @@ -32,7 +33,7 @@ public AsyncQueryingEnumerable( ISqlExpressionFactory sqlExpressionFactory, IParameterNameGeneratorFactory parameterNameGeneratorFactory, SelectExpression selectExpression, - Func> shaper, + Func> shaper, Type contextType, IDiagnosticsLogger logger) { @@ -52,10 +53,11 @@ public IAsyncEnumerator GetAsyncEnumerator(CancellationToken cancellationToke private sealed class AsyncEnumerator : IAsyncEnumerator { private RelationalDataReader _dataReader; + private int[] _indexMap; private ResultCoordinator _resultCoordinator; private readonly RelationalQueryContext _relationalQueryContext; private readonly SelectExpression _selectExpression; - private readonly Func> _shaper; + private readonly Func> _shaper; private readonly IQuerySqlGeneratorFactory _querySqlGeneratorFactory; private readonly Type _contextType; private readonly IDiagnosticsLogger _logger; @@ -104,6 +106,47 @@ public async ValueTask MoveNextAsync() _relationalQueryContext.CommandLogger, _cancellationToken); + if (selectExpression.IsNonComposedFromSql()) + { + var projection = _selectExpression.Projection.ToList(); + var readerColumns = Enumerable.Range(0, _dataReader.DbDataReader.FieldCount) + .Select( + i => new + { + Name = _dataReader.DbDataReader.GetName(i), + Ordinal = i + }).ToList(); + + _indexMap = new int[projection.Count]; + + for (var i = 0; i < projection.Count; i++) + { + if (projection[i].Expression is ColumnExpression columnExpression) + { + var columnName = columnExpression.Name; + + if (columnName != null) + { + var readerColumn + = readerColumns.SingleOrDefault( + c => + string.Equals(columnName, c.Name, StringComparison.OrdinalIgnoreCase)); + + if (readerColumn == null) + { + throw new InvalidOperationException(RelationalStrings.FromSqlMissingColumn(columnName)); + } + + _indexMap[i] = readerColumn.Ordinal; + } + } + } + } + else + { + _indexMap = null; + } + _resultCoordinator = new ResultCoordinator(); } catch (Exception) @@ -121,7 +164,7 @@ public async ValueTask MoveNextAsync() Current = hasNext - ? await _shaper(_relationalQueryContext, _dataReader.DbDataReader, _resultCoordinator) + ? await _shaper(_relationalQueryContext, _dataReader.DbDataReader, _indexMap, _resultCoordinator) : default; return hasNext; diff --git a/src/EFCore.Relational/Query/Pipeline/FromSqlNonComposedAsyncQueryingEnumerable.cs b/src/EFCore.Relational/Query/Pipeline/FromSqlNonComposedAsyncQueryingEnumerable.cs deleted file mode 100644 index 8633b1f5e9b..00000000000 --- a/src/EFCore.Relational/Query/Pipeline/FromSqlNonComposedAsyncQueryingEnumerable.cs +++ /dev/null @@ -1,181 +0,0 @@ -// 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.Data.Common; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.EntityFrameworkCore.Diagnostics; -using Microsoft.EntityFrameworkCore.Query; -using Microsoft.EntityFrameworkCore.Relational.Query.Pipeline.SqlExpressions; -using Microsoft.EntityFrameworkCore.Storage; - -namespace Microsoft.EntityFrameworkCore.Relational.Query.Pipeline -{ - public partial class RelationalShapedQueryCompilingExpressionVisitor - { - private class FromSqlNonComposedAsyncQueryingEnumerable : IAsyncEnumerable - { - private readonly RelationalQueryContext _relationalQueryContext; - private readonly SelectExpression _selectExpression; - private readonly Func> _shaper; - private readonly IQuerySqlGeneratorFactory _querySqlGeneratorFactory; - private readonly Type _contextType; - private readonly IDiagnosticsLogger _logger; - private readonly ISqlExpressionFactory _sqlExpressionFactory; - private readonly IParameterNameGeneratorFactory _parameterNameGeneratorFactory; - - public FromSqlNonComposedAsyncQueryingEnumerable( - RelationalQueryContext relationalQueryContext, - IQuerySqlGeneratorFactory querySqlGeneratorFactory, - ISqlExpressionFactory sqlExpressionFactory, - IParameterNameGeneratorFactory parameterNameGeneratorFactory, - SelectExpression selectExpression, - Func> shaper, - Type contextType, - IDiagnosticsLogger logger) - { - _relationalQueryContext = relationalQueryContext; - _querySqlGeneratorFactory = querySqlGeneratorFactory; - _sqlExpressionFactory = sqlExpressionFactory; - _parameterNameGeneratorFactory = parameterNameGeneratorFactory; - _selectExpression = selectExpression; - _shaper = shaper; - _contextType = contextType; - _logger = logger; - } - - public IAsyncEnumerator GetAsyncEnumerator(CancellationToken cancellationToken = default) - => new AsyncEnumerator(this, cancellationToken); - - private sealed class AsyncEnumerator : IAsyncEnumerator - { - private RelationalDataReader _dataReader; - private int[] _indexMap; - private readonly RelationalQueryContext _relationalQueryContext; - private readonly SelectExpression _selectExpression; - private readonly Func> _shaper; - private readonly IQuerySqlGeneratorFactory _querySqlGeneratorFactory; - private readonly Type _contextType; - private readonly IDiagnosticsLogger _logger; - private readonly ISqlExpressionFactory _sqlExpressionFactory; - private readonly IParameterNameGeneratorFactory _parameterNameGeneratorFactory; - private readonly CancellationToken _cancellationToken; - - public AsyncEnumerator( - FromSqlNonComposedAsyncQueryingEnumerable queryingEnumerable, - CancellationToken cancellationToken) - { - _relationalQueryContext = queryingEnumerable._relationalQueryContext; - _shaper = queryingEnumerable._shaper; - _selectExpression = queryingEnumerable._selectExpression; - _querySqlGeneratorFactory = queryingEnumerable._querySqlGeneratorFactory; - _contextType = queryingEnumerable._contextType; - _logger = queryingEnumerable._logger; - _sqlExpressionFactory = queryingEnumerable._sqlExpressionFactory; - _parameterNameGeneratorFactory = queryingEnumerable._parameterNameGeneratorFactory; - _cancellationToken = cancellationToken; - } - - public T Current { get; private set; } - - public async ValueTask MoveNextAsync() - { - try - { - if (_dataReader == null) - { - _relationalQueryContext.Connection.Open(); - - try - { - var projection = _selectExpression.Projection.ToList(); - - var selectExpression = new ParameterValueBasedSelectExpressionOptimizer( - _sqlExpressionFactory, - _parameterNameGeneratorFactory) - .Optimize(_selectExpression, _relationalQueryContext.ParameterValues); - - var relationalCommand = _querySqlGeneratorFactory.Create().GetCommand(selectExpression); - - _dataReader - = await relationalCommand.ExecuteReaderAsync( - _relationalQueryContext.Connection, - _relationalQueryContext.ParameterValues, - _relationalQueryContext.CommandLogger, - _cancellationToken); - - var readerColumns = Enumerable.Range(0, _dataReader.DbDataReader.FieldCount) - .Select( - i => new - { - Name = _dataReader.DbDataReader.GetName(i), - Ordinal = i - }).ToList(); - - _indexMap = new int[projection.Count]; - - for (var i = 0; i < projection.Count; i++) - { - if (projection[i].Expression is ColumnExpression columnExpression) - { - var columnName = columnExpression.Name; - - if (columnName != null) - { - var readerColumn - = readerColumns.SingleOrDefault( - c => - string.Equals(columnName, c.Name, StringComparison.OrdinalIgnoreCase)); - - if (readerColumn == null) - { - throw new InvalidOperationException(RelationalStrings.FromSqlMissingColumn(columnName)); - } - - _indexMap[i] = readerColumn.Ordinal; - } - } - } - } - catch (Exception) - { - // If failure happens creating the data reader, then it won't be available to - // handle closing the connection, so do it explicitly here to preserve ref counting. - _relationalQueryContext.Connection.Close(); - - throw; - } - } - - var hasNext = await _dataReader.ReadAsync(_cancellationToken); - - Current - = hasNext - ? await _shaper(_relationalQueryContext, _dataReader.DbDataReader, _indexMap) - : default; - - return hasNext; - } - catch (Exception exception) - { - _logger.QueryIterationFailed(_contextType, exception); - - throw; - } - } - - public ValueTask DisposeAsync() - { - _dataReader?.Dispose(); - _dataReader = null; - _relationalQueryContext.Connection.Close(); - - return default; - } - } - } - } -} diff --git a/src/EFCore.Relational/Query/Pipeline/FromSqlNonComposedQueryingEnumerable.cs b/src/EFCore.Relational/Query/Pipeline/FromSqlNonComposedQueryingEnumerable.cs deleted file mode 100644 index c00e58c24a7..00000000000 --- a/src/EFCore.Relational/Query/Pipeline/FromSqlNonComposedQueryingEnumerable.cs +++ /dev/null @@ -1,177 +0,0 @@ -// 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; -using System.Collections.Generic; -using System.Data.Common; -using System.Linq; -using Microsoft.EntityFrameworkCore.Diagnostics; -using Microsoft.EntityFrameworkCore.Query; -using Microsoft.EntityFrameworkCore.Relational.Query.Pipeline.SqlExpressions; -using Microsoft.EntityFrameworkCore.Storage; - -namespace Microsoft.EntityFrameworkCore.Relational.Query.Pipeline -{ - public partial class RelationalShapedQueryCompilingExpressionVisitor - { - private class FromSqlNonComposedQueryingEnumerable : IEnumerable - { - private readonly RelationalQueryContext _relationalQueryContext; - private readonly SelectExpression _selectExpression; - private readonly Func _shaper; - private readonly IQuerySqlGeneratorFactory _querySqlGeneratorFactory; - private readonly Type _contextType; - private readonly IDiagnosticsLogger _logger; - private readonly ISqlExpressionFactory _sqlExpressionFactory; - private readonly IParameterNameGeneratorFactory _parameterNameGeneratorFactory; - - public FromSqlNonComposedQueryingEnumerable( - RelationalQueryContext relationalQueryContext, - IQuerySqlGeneratorFactory querySqlGeneratorFactory, - ISqlExpressionFactory sqlExpressionFactory, - IParameterNameGeneratorFactory parameterNameGeneratorFactory, - SelectExpression selectExpression, - Func shaper, - Type contextType, - IDiagnosticsLogger logger) - { - _relationalQueryContext = relationalQueryContext; - _querySqlGeneratorFactory = querySqlGeneratorFactory; - _sqlExpressionFactory = sqlExpressionFactory; - _parameterNameGeneratorFactory = parameterNameGeneratorFactory; - _selectExpression = selectExpression; - _shaper = shaper; - _contextType = contextType; - _logger = logger; - } - - public IEnumerator GetEnumerator() => new Enumerator(this); - IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); - - private sealed class Enumerator : IEnumerator - { - private RelationalDataReader _dataReader; - private int[] _indexMap; - private readonly RelationalQueryContext _relationalQueryContext; - private readonly SelectExpression _selectExpression; - private readonly Func _shaper; - private readonly IQuerySqlGeneratorFactory _querySqlGeneratorFactory; - private readonly Type _contextType; - private readonly IDiagnosticsLogger _logger; - private readonly ISqlExpressionFactory _sqlExpressionFactory; - private readonly IParameterNameGeneratorFactory _parameterNameGeneratorFactory; - - public Enumerator(FromSqlNonComposedQueryingEnumerable queryingEnumerable) - { - _relationalQueryContext = queryingEnumerable._relationalQueryContext; - _shaper = queryingEnumerable._shaper; - _selectExpression = queryingEnumerable._selectExpression; - _querySqlGeneratorFactory = queryingEnumerable._querySqlGeneratorFactory; - _contextType = queryingEnumerable._contextType; - _logger = queryingEnumerable._logger; - _sqlExpressionFactory = queryingEnumerable._sqlExpressionFactory; - _parameterNameGeneratorFactory = queryingEnumerable._parameterNameGeneratorFactory; - } - - public T Current { get; private set; } - - object IEnumerator.Current => Current; - - public void Dispose() - { - _dataReader?.Dispose(); - _dataReader = null; - _relationalQueryContext.Connection.Close(); - } - - public bool MoveNext() - { - try - { - if (_dataReader == null) - { - _relationalQueryContext.Connection.Open(); - - try - { - var projection = _selectExpression.Projection.ToList(); - - var selectExpression = new ParameterValueBasedSelectExpressionOptimizer( - _sqlExpressionFactory, - _parameterNameGeneratorFactory) - .Optimize(_selectExpression, _relationalQueryContext.ParameterValues); - - var relationalCommand = _querySqlGeneratorFactory.Create().GetCommand(selectExpression); - - _dataReader - = relationalCommand.ExecuteReader( - _relationalQueryContext.Connection, - _relationalQueryContext.ParameterValues, - _relationalQueryContext.CommandLogger); - - var readerColumns = Enumerable.Range(0, _dataReader.DbDataReader.FieldCount) - .Select( - i => new - { - Name = _dataReader.DbDataReader.GetName(i), - Ordinal = i - }).ToList(); - - _indexMap = new int[projection.Count]; - - for (var i = 0; i < projection.Count; i++) - { - if (projection[i].Expression is ColumnExpression columnExpression) - { - var columnName = columnExpression.Name; - - if (columnName != null) - { - var readerColumn - = readerColumns.SingleOrDefault( - c => - string.Equals(columnName, c.Name, StringComparison.OrdinalIgnoreCase)); - - if (readerColumn == null) - { - throw new InvalidOperationException(RelationalStrings.FromSqlMissingColumn(columnName)); - } - - _indexMap[i] = readerColumn.Ordinal; - } - } - } - } - catch (Exception) - { - // If failure happens creating the data reader, then it won't be available to - // handle closing the connection, so do it explicitly here to preserve ref counting. - _relationalQueryContext.Connection.Close(); - - throw; - } - } - - var hasNext = _dataReader.Read(); - - Current - = hasNext - ? _shaper(_relationalQueryContext, _dataReader.DbDataReader, _indexMap) - : default; - - return hasNext; - } - catch (Exception exception) - { - _logger.QueryIterationFailed(_contextType, exception); - - throw; - } - } - - public void Reset() => throw new NotImplementedException(); - } - } - } -} diff --git a/src/EFCore.Relational/Query/Pipeline/QueryingEnumerable.cs b/src/EFCore.Relational/Query/Pipeline/QueryingEnumerable.cs index 16335b0285c..b40f0ea8d22 100644 --- a/src/EFCore.Relational/Query/Pipeline/QueryingEnumerable.cs +++ b/src/EFCore.Relational/Query/Pipeline/QueryingEnumerable.cs @@ -5,6 +5,7 @@ using System.Collections; using System.Collections.Generic; using System.Data.Common; +using System.Linq; using Microsoft.EntityFrameworkCore.Diagnostics; using Microsoft.EntityFrameworkCore.Query; using Microsoft.EntityFrameworkCore.Relational.Query.Pipeline.SqlExpressions; @@ -18,7 +19,7 @@ private class QueryingEnumerable : IEnumerable { private readonly RelationalQueryContext _relationalQueryContext; private readonly SelectExpression _selectExpression; - private readonly Func _shaper; + private readonly Func _shaper; private readonly IQuerySqlGeneratorFactory _querySqlGeneratorFactory; private readonly Type _contextType; private readonly IDiagnosticsLogger _logger; @@ -30,7 +31,7 @@ public QueryingEnumerable(RelationalQueryContext relationalQueryContext, ISqlExpressionFactory sqlExpressionFactory, IParameterNameGeneratorFactory parameterNameGeneratorFactory, SelectExpression selectExpression, - Func shaper, + Func shaper, Type contextType, IDiagnosticsLogger logger) { @@ -50,10 +51,11 @@ public QueryingEnumerable(RelationalQueryContext relationalQueryContext, private sealed class Enumerator : IEnumerator { private RelationalDataReader _dataReader; + private int[] _indexMap; private ResultCoordinator _resultCoordinator; private readonly RelationalQueryContext _relationalQueryContext; private readonly SelectExpression _selectExpression; - private readonly Func _shaper; + private readonly Func _shaper; private readonly IQuerySqlGeneratorFactory _querySqlGeneratorFactory; private readonly Type _contextType; private readonly IDiagnosticsLogger _logger; @@ -106,6 +108,47 @@ public bool MoveNext() _relationalQueryContext.ParameterValues, _relationalQueryContext.CommandLogger); + if (selectExpression.IsNonComposedFromSql()) + { + var projection = _selectExpression.Projection.ToList(); + var readerColumns = Enumerable.Range(0, _dataReader.DbDataReader.FieldCount) + .Select( + i => new + { + Name = _dataReader.DbDataReader.GetName(i), + Ordinal = i + }).ToList(); + + _indexMap = new int[projection.Count]; + + for (var i = 0; i < projection.Count; i++) + { + if (projection[i].Expression is ColumnExpression columnExpression) + { + var columnName = columnExpression.Name; + + if (columnName != null) + { + var readerColumn + = readerColumns.SingleOrDefault( + c => + string.Equals(columnName, c.Name, StringComparison.OrdinalIgnoreCase)); + + if (readerColumn == null) + { + throw new InvalidOperationException(RelationalStrings.FromSqlMissingColumn(columnName)); + } + + _indexMap[i] = readerColumn.Ordinal; + } + } + } + } + else + { + _indexMap = null; + } + _resultCoordinator = new ResultCoordinator(); } catch (Exception) @@ -123,7 +166,7 @@ public bool MoveNext() Current = hasNext - ? _shaper(_relationalQueryContext, _dataReader.DbDataReader, _resultCoordinator) + ? _shaper(_relationalQueryContext, _dataReader.DbDataReader, _indexMap, _resultCoordinator) : default; return hasNext; diff --git a/src/EFCore.Relational/Query/Pipeline/RelationalShapedQueryCompilingExpressionVisitor.cs b/src/EFCore.Relational/Query/Pipeline/RelationalShapedQueryCompilingExpressionVisitor.cs index f17665a5ff0..bee53a7e2f1 100644 --- a/src/EFCore.Relational/Query/Pipeline/RelationalShapedQueryCompilingExpressionVisitor.cs +++ b/src/EFCore.Relational/Query/Pipeline/RelationalShapedQueryCompilingExpressionVisitor.cs @@ -49,38 +49,19 @@ protected override Expression VisitShapedQueryExpression(ShapedQueryExpression s var selectExpression = (SelectExpression)shapedQueryExpression.QueryExpression; shaperBody = new RelationalProjectionBindingRemovingExpressionVisitor(selectExpression).Visit(shaperBody); - shaperBody = new IncludeCompilingExpressionVisitor(TrackQueryResults).Visit(shaperBody); + var indexMapParameter = Expression.Parameter(typeof(int[]), "indexMap"); if (selectExpression.IsNonComposedFromSql()) { - var indexMapParameter = Expression.Parameter(typeof(int[]), "indexMap"); shaperBody = new IndexMapInjectingExpressionVisitor(indexMapParameter).Visit(shaperBody); - var remappedShaperLambda = - Expression.Lambda( - shaperBody, - QueryCompilationContext.QueryContextParameter, - RelationalProjectionBindingRemovingExpressionVisitor.DataReaderParameter, - indexMapParameter); - - return Expression.New( - Async - ? typeof(FromSqlNonComposedAsyncQueryingEnumerable<>).MakeGenericType(remappedShaperLambda.ReturnType.GetGenericArguments().Single()).GetConstructors()[0] - : typeof(FromSqlNonComposedQueryingEnumerable<>).MakeGenericType(remappedShaperLambda.ReturnType).GetConstructors()[0], - Expression.Convert(QueryCompilationContext.QueryContextParameter, typeof(RelationalQueryContext)), - Expression.Constant(_querySqlGeneratorFactory), - Expression.Constant(_sqlExpressionFactory), - Expression.Constant(_parameterNameGeneratorFactory), - Expression.Constant(selectExpression), - Expression.Constant(remappedShaperLambda.Compile()), - Expression.Constant(_contextType), - Expression.Constant(_logger)); } var shaperLambda = Expression.Lambda( shaperBody, QueryCompilationContext.QueryContextParameter, RelationalProjectionBindingRemovingExpressionVisitor.DataReaderParameter, + indexMapParameter, _resultCoordinatorParameter); if (Async)