SqlExpressionFactory can't be used inside SqlGenerator so create the expressions manually. Fixes queries with conversions

pull/131/head
Christopher Jolly 3 years ago
parent ca0feb49d3
commit b533695713

@ -2,17 +2,21 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using EntityFrameworkCore.Jet.Data; using EntityFrameworkCore.Jet.Data;
using System.Linq; using System.Linq;
using System.Linq.Expressions; using System.Linq.Expressions;
using EntityFrameworkCore.Jet.Infrastructure.Internal; using EntityFrameworkCore.Jet.Infrastructure.Internal;
using JetBrains.Annotations;
using Microsoft.EntityFrameworkCore.Storage; using Microsoft.EntityFrameworkCore.Storage;
using EntityFrameworkCore.Jet.Utilities; using EntityFrameworkCore.Jet.Utilities;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Diagnostics; using Microsoft.EntityFrameworkCore.Diagnostics;
using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Query; using Microsoft.EntityFrameworkCore.Query;
using Microsoft.EntityFrameworkCore.Query.SqlExpressions; using Microsoft.EntityFrameworkCore.Query.SqlExpressions;
using ExpressionExtensions = Microsoft.EntityFrameworkCore.Internal.ExpressionExtensions;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Metadata.Internal;
namespace EntityFrameworkCore.Jet.Query.Sql.Internal namespace EntityFrameworkCore.Jet.Query.Sql.Internal
{ {
@ -24,36 +28,40 @@ namespace EntityFrameworkCore.Jet.Query.Sql.Internal
{ {
private static readonly Dictionary<string, string> _convertMappings = new Dictionary<string, string> private static readonly Dictionary<string, string> _convertMappings = new Dictionary<string, string>
{ {
{nameof(Boolean), "CBOOL"}, { nameof(Boolean), "CBOOL" },
{nameof(Byte), "CBYTE"}, { nameof(Byte), "CBYTE" },
{nameof(SByte), "CINT"}, { nameof(SByte), "CINT" },
{nameof(Int16), "CINT"}, { nameof(Int16), "CINT" },
{nameof(Int32), "CLNG"}, { nameof(Int32), "CLNG" },
{nameof(Single), "CSNG"}, { nameof(Single), "CSNG" },
{nameof(Double), "CDBL"}, { nameof(Double), "CDBL" },
{nameof(Decimal), "CCUR"}, { nameof(Decimal), "CCUR" },
{nameof(DateTime), "CDATE"}, { nameof(DateTime), "CDATE" },
}; };
private readonly ITypeMappingSource _typeMappingSource; private readonly ITypeMappingSource _typeMappingSource;
private readonly IJetOptions _options; private readonly IJetOptions _options;
private readonly ISqlGenerationHelper _sqlGenerationHelper; private readonly ISqlGenerationHelper _sqlGenerationHelper;
//private readonly JetSqlExpressionFactory _sqlExpressionFactory;
private CoreTypeMapping _boolTypeMapping; private CoreTypeMapping _boolTypeMapping;
/// <summary> /// <summary>
/// This API supports the Entity Framework Core infrastructure and is not intended to be used /// This API supports the Entity Framework Core infrastructure and is not intended to be used
/// directly from your code. This API may change or be removed in future releases. /// directly from your code. This API may change or be removed in future releases.
/// </summary> /// </summary>
public JetQuerySqlGenerator( public JetQuerySqlGenerator(
[NotNull] QuerySqlGeneratorDependencies dependencies, [JetBrains.Annotations.NotNull] QuerySqlGeneratorDependencies dependencies,
ITypeMappingSource typeMappingSource, //ISqlExpressionFactory sqlExpressionFactory,
[JetBrains.Annotations.NotNull] ITypeMappingSource typeMappingSource,
IJetOptions options) IJetOptions options)
: base(dependencies) : base(dependencies)
{ {
//_sqlExpressionFactory = (JetSqlExpressionFactory)sqlExpressionFactory;
_typeMappingSource = typeMappingSource; _typeMappingSource = typeMappingSource;
_options = options; _options = options;
_sqlGenerationHelper = dependencies.SqlGenerationHelper; _sqlGenerationHelper = dependencies.SqlGenerationHelper;
_boolTypeMapping = _typeMappingSource.FindMapping(typeof(bool)); _boolTypeMapping = _typeMappingSource.FindMapping(typeof(bool));
//_jetSqlExpressionFactory = jetSqlExpressionFactory;
} }
protected override Expression VisitSelect(SelectExpression selectExpression) protected override Expression VisitSelect(SelectExpression selectExpression)
@ -65,7 +73,7 @@ namespace EntityFrameworkCore.Jet.Query.Sql.Internal
if (IsNonComposedSetOperation(selectExpression)) if (IsNonComposedSetOperation(selectExpression))
{ {
// Naked set operation // Naked set operation
GenerateSetOperation((SetOperationBase) selectExpression.Tables[0]); GenerateSetOperation((SetOperationBase)selectExpression.Tables[0]);
return selectExpression; return selectExpression;
} }
@ -112,7 +120,8 @@ namespace EntityFrameworkCore.Jet.Query.Sql.Internal
0, 0,
selectExpression selectExpression
.Tables .Tables
.Count(t => !(t is CrossJoinExpression || t is CrossApplyExpression)) - maxTablesWithoutBrackets))); .Count(t => !(t is CrossJoinExpression || t is CrossApplyExpression)) -
maxTablesWithoutBrackets)));
for (var index = 0; index < selectExpression.Tables.Count; index++) for (var index = 0; index < selectExpression.Tables.Count; index++)
{ {
@ -126,7 +135,8 @@ namespace EntityFrameworkCore.Jet.Query.Sql.Internal
if (isApplyExpression) if (isApplyExpression)
{ {
throw new InvalidOperationException("Jet does not support APPLY statements. Switch to client evaluation explicitly by inserting a call to either AsEnumerable(), AsAsyncEnumerable(), ToList(), or ToListAsync() if needed."); throw new InvalidOperationException(
"Jet does not support APPLY statements. Switch to client evaluation explicitly by inserting a call to either AsEnumerable(), AsAsyncEnumerable(), ToList(), or ToListAsync() if needed.");
} }
if (index > 0) if (index > 0)
@ -202,7 +212,8 @@ namespace EntityFrameworkCore.Jet.Query.Sql.Internal
&& selectExpression.Projection.Count == setOperation.Source1.Projection.Count && selectExpression.Projection.Count == setOperation.Source1.Projection.Count
&& selectExpression.Projection.Select( && selectExpression.Projection.Select(
(pe, index) => pe.Expression is ColumnExpression column (pe, index) => pe.Expression is ColumnExpression column
&& string.Equals(column.Table.Alias, setOperation.Alias, StringComparison.OrdinalIgnoreCase) && string.Equals(column.Table.Alias, setOperation.Alias,
StringComparison.OrdinalIgnoreCase)
&& string.Equals( && string.Equals(
column.Name, setOperation.Source1.Projection[index] column.Name, setOperation.Source1.Projection[index]
.Alias, StringComparison.OrdinalIgnoreCase)) .Alias, StringComparison.OrdinalIgnoreCase))
@ -252,10 +263,10 @@ namespace EntityFrameworkCore.Jet.Query.Sql.Internal
{ {
Check.NotNull(sqlBinaryExpression, nameof(sqlBinaryExpression)); Check.NotNull(sqlBinaryExpression, nameof(sqlBinaryExpression));
/*if (sqlBinaryExpression.OperatorType == ExpressionType.Coalesce) if (sqlBinaryExpression.OperatorType == ExpressionType.Coalesce)
{ {
Visit( //Visit(
_sqlExpressionFactory.Case( /*_sqlExpressionFactory.Case(
new[] new[]
{ {
new CaseWhenClause( new CaseWhenClause(
@ -263,8 +274,18 @@ namespace EntityFrameworkCore.Jet.Query.Sql.Internal
sqlBinaryExpression.Right) sqlBinaryExpression.Right)
}, },
sqlBinaryExpression.Left)); sqlBinaryExpression.Left));
return sqlBinaryExpression;*/
SqlConstantExpression nullcons = new SqlConstantExpression(Expression.Constant(null), RelationalTypeMapping.NullMapping);
SqlUnaryExpression isnullexp = new SqlUnaryExpression(ExpressionType.Equal, sqlBinaryExpression.Left, typeof(bool), null);
List<CaseWhenClause> whenclause = new List<CaseWhenClause>
{
new CaseWhenClause(isnullexp, sqlBinaryExpression.Right)
};
CaseExpression caseexp = new CaseExpression(whenclause, sqlBinaryExpression.Left);
Visit(caseexp);
return sqlBinaryExpression; return sqlBinaryExpression;
}*/ }
return base.VisitSqlBinary(sqlBinaryExpression); return base.VisitSqlBinary(sqlBinaryExpression);
} }
@ -279,23 +300,37 @@ namespace EntityFrameworkCore.Jet.Query.Sql.Internal
var typeMapping = convertExpression.TypeMapping; var typeMapping = convertExpression.TypeMapping;
if (typeMapping == null) if (typeMapping == null)
throw new InvalidOperationException(RelationalStrings.UnsupportedType(convertExpression.Type.ShortDisplayName())); throw new InvalidOperationException(
RelationalStrings.UnsupportedType(convertExpression.Type.ShortDisplayName()));
// We are explicitly converting to the target type (convertExpression.Type) and not the CLR type of the // We are explicitly converting to the target type (convertExpression.Type) and not the CLR type of the
// accociated type mapping. This allows for conversions on the database side (e.g. CDBL()) but handling // accociated type mapping. This allows for conversions on the database side (e.g. CDBL()) but handling
// of the returned value using a different (unaligned) type mapping (e.g. date/time related ones). // of the returned value using a different (unaligned) type mapping (e.g. date/time related ones).
if (_convertMappings.TryGetValue(convertExpression.Type.Name, out var function)) if (_convertMappings.TryGetValue(convertExpression.Type.Name, out var function))
{ {
/*Visit( /* Visit(
_sqlExpressionFactory.NullChecked( _sqlExpressionFactory.NullChecked(
convertExpression.Operand, convertExpression.Operand,
_sqlExpressionFactory.Function( _sqlExpressionFactory.Function(
function, function,
new[] {convertExpression.Operand}, new[] { convertExpression.Operand },
false, false,
new[] {false}, new[] { false },
typeMapping.ClrType))); typeMapping.ClrType)));
*/ */
SqlExpression checksqlexp = convertExpression.Operand;
SqlFunctionExpression notnullsqlexp = new SqlFunctionExpression(function, new SqlExpression[] { convertExpression.Operand },
false, new[] { false }, typeMapping.ClrType,null);
SqlConstantExpression nullcons = new SqlConstantExpression(Expression.Constant(null),RelationalTypeMapping.NullMapping);
SqlUnaryExpression isnullexp = new SqlUnaryExpression(ExpressionType.Equal, checksqlexp, typeof(bool), null);
List<CaseWhenClause> whenclause = new List<CaseWhenClause>
{
new CaseWhenClause(isnullexp, nullcons)
};
CaseExpression caseexp = new CaseExpression(whenclause, notnullsqlexp);
Visit(caseexp);
return convertExpression; return convertExpression;
} }
@ -325,7 +360,7 @@ namespace EntityFrameworkCore.Jet.Query.Sql.Internal
return likeExpression; return likeExpression;
} }
protected override string GetOperator([NotNull] SqlBinaryExpression binaryExpression) protected override string GetOperator([JetBrains.Annotations.NotNull] SqlBinaryExpression binaryExpression)
=> binaryExpression.OperatorType switch => binaryExpression.OperatorType switch
{ {
ExpressionType.Add when binaryExpression.Type == typeof(string) => " & ", ExpressionType.Add when binaryExpression.Type == typeof(string) => " & ",
@ -357,7 +392,8 @@ namespace EntityFrameworkCore.Jet.Query.Sql.Internal
} }
else else
{ {
throw new InvalidOperationException("Jet does not support skipping rows. Switch to client evaluation explicitly by inserting a call to either AsEnumerable(), AsAsyncEnumerable(), ToList(), or ToListAsync() if needed."); throw new InvalidOperationException(
"Jet does not support skipping rows. Switch to client evaluation explicitly by inserting a call to either AsEnumerable(), AsAsyncEnumerable(), ToList(), or ToListAsync() if needed.");
} }
} }
@ -441,7 +477,5 @@ namespace EntityFrameworkCore.Jet.Query.Sql.Internal
protected override Expression VisitRowNumber(RowNumberExpression rowNumberExpression) protected override Expression VisitRowNumber(RowNumberExpression rowNumberExpression)
=> throw new InvalidOperationException(CoreStrings.TranslationFailed(rowNumberExpression)); => throw new InvalidOperationException(CoreStrings.TranslationFailed(rowNumberExpression));
} }
} }
Loading…
Cancel
Save