diff --git a/src/EFCore.SqlServer/Query/Internal/SqlServerByteArrayMethodTranslator.cs b/src/EFCore.SqlServer/Query/Internal/SqlServerByteArrayMethodTranslator.cs
index 349e245e70c..92384cab6d7 100644
--- a/src/EFCore.SqlServer/Query/Internal/SqlServerByteArrayMethodTranslator.cs
+++ b/src/EFCore.SqlServer/Query/Internal/SqlServerByteArrayMethodTranslator.cs
@@ -72,6 +72,25 @@ public SqlServerByteArrayMethodTranslator([NotNull] ISqlExpressionFactory sqlExp
_sqlExpressionFactory.Constant(0));
}
+ if (method.IsGenericMethod
+ && method.GetGenericMethodDefinition().Equals(EnumerableMethods.FirstWithoutPredicate)
+ && arguments[0].Type == typeof(byte[]))
+ {
+ return _sqlExpressionFactory.Convert(
+ _sqlExpressionFactory.Function(
+ "SUBSTRING",
+ new SqlExpression[]
+ {
+ arguments[0],
+ _sqlExpressionFactory.Constant(1),
+ _sqlExpressionFactory.Constant(1)
+ },
+ nullable: true,
+ argumentsPropagateNullability: new[] { true, true, true },
+ typeof(byte[])),
+ method.ReturnType);
+ }
+
return null;
}
}
diff --git a/src/EFCore.SqlServer/Query/Internal/SqlServerSqlTranslatingExpressionVisitor.cs b/src/EFCore.SqlServer/Query/Internal/SqlServerSqlTranslatingExpressionVisitor.cs
index a4338430569..f84250b6b63 100644
--- a/src/EFCore.SqlServer/Query/Internal/SqlServerSqlTranslatingExpressionVisitor.cs
+++ b/src/EFCore.SqlServer/Query/Internal/SqlServerSqlTranslatingExpressionVisitor.cs
@@ -64,6 +64,33 @@ protected override Expression VisitBinary(BinaryExpression binaryExpression)
{
Check.NotNull(binaryExpression, nameof(binaryExpression));
+ if (binaryExpression.NodeType == ExpressionType.ArrayIndex
+ && binaryExpression.Left.Type == typeof(byte[]))
+ {
+ var left = Visit(binaryExpression.Left);
+ var right = Visit(binaryExpression.Right);
+
+ if (left is SqlExpression leftSql
+ && right is SqlExpression rightSql)
+ {
+ return Dependencies.SqlExpressionFactory.Convert(
+ Dependencies.SqlExpressionFactory.Function(
+ "SUBSTRING",
+ new SqlExpression[]
+ {
+ leftSql,
+ Dependencies.SqlExpressionFactory.Add(
+ Dependencies.SqlExpressionFactory.ApplyDefaultTypeMapping(rightSql),
+ Dependencies.SqlExpressionFactory.Constant(1)),
+ Dependencies.SqlExpressionFactory.Constant(1)
+ },
+ nullable: true,
+ argumentsPropagateNullability: new[] { true, true, true },
+ typeof(byte[])),
+ binaryExpression.Type);
+ }
+ }
+
return !(base.VisitBinary(binaryExpression) is SqlExpression visitedExpression)
? QueryCompilationContext.NotTranslatedExpression
: visitedExpression is SqlBinaryExpression sqlBinary
diff --git a/src/EFCore.Sqlite.Core/Query/Internal/SqliteByteArrayMethodTranslator.cs b/src/EFCore.Sqlite.Core/Query/Internal/SqliteByteArrayMethodTranslator.cs
index bc828f2c319..7630acc9d89 100644
--- a/src/EFCore.Sqlite.Core/Query/Internal/SqliteByteArrayMethodTranslator.cs
+++ b/src/EFCore.Sqlite.Core/Query/Internal/SqliteByteArrayMethodTranslator.cs
@@ -8,6 +8,7 @@
using Microsoft.EntityFrameworkCore.Diagnostics;
using Microsoft.EntityFrameworkCore.Query;
using Microsoft.EntityFrameworkCore.Query.SqlExpressions;
+using Microsoft.EntityFrameworkCore.Storage;
using Microsoft.EntityFrameworkCore.Utilities;
#nullable enable
@@ -23,6 +24,7 @@ namespace Microsoft.EntityFrameworkCore.Sqlite.Query.Internal
public class SqliteByteArrayMethodTranslator : IMethodCallTranslator
{
private readonly ISqlExpressionFactory _sqlExpressionFactory;
+ private readonly IRelationalTypeMappingSource _typeMappingSource;
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
@@ -30,9 +32,12 @@ public class SqliteByteArrayMethodTranslator : IMethodCallTranslator
/// 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.
///
- public SqliteByteArrayMethodTranslator([NotNull] ISqlExpressionFactory sqlExpressionFactory)
+ public SqliteByteArrayMethodTranslator(
+ [NotNull] ISqlExpressionFactory sqlExpressionFactory,
+ [NotNull] IRelationalTypeMappingSource typeMappingSource)
{
_sqlExpressionFactory = sqlExpressionFactory;
+ _typeMappingSource = typeMappingSource;
}
///
@@ -76,6 +81,32 @@ public SqliteByteArrayMethodTranslator([NotNull] ISqlExpressionFactory sqlExpres
_sqlExpressionFactory.Constant(0));
}
+ // See issue#16428
+ //if (method.IsGenericMethod
+ // && method.GetGenericMethodDefinition().Equals(EnumerableMethods.FirstWithoutPredicate)
+ // && arguments[0].Type == typeof(byte[]))
+ //{
+ // return _sqlExpressionFactory.Function(
+ // "unicode",
+ // new SqlExpression[]
+ // {
+ // _sqlExpressionFactory.Function(
+ // "substr",
+ // new SqlExpression[]
+ // {
+ // arguments[0],
+ // _sqlExpressionFactory.Constant(1),
+ // _sqlExpressionFactory.Constant(1)
+ // },
+ // nullable: true,
+ // argumentsPropagateNullability: new[] { true, true, true },
+ // typeof(byte[]))
+ // },
+ // nullable: true,
+ // argumentsPropagateNullability: new[] { true },
+ // method.ReturnType);
+ //}
+
return null;
}
}
diff --git a/src/EFCore.Sqlite.Core/Query/Internal/SqliteMethodCallTranslatorProvider.cs b/src/EFCore.Sqlite.Core/Query/Internal/SqliteMethodCallTranslatorProvider.cs
index 32b273e0d80..f58a5a74008 100644
--- a/src/EFCore.Sqlite.Core/Query/Internal/SqliteMethodCallTranslatorProvider.cs
+++ b/src/EFCore.Sqlite.Core/Query/Internal/SqliteMethodCallTranslatorProvider.cs
@@ -30,7 +30,7 @@ public SqliteMethodCallTranslatorProvider([NotNull] RelationalMethodCallTranslat
AddTranslators(
new IMethodCallTranslator[]
{
- new SqliteByteArrayMethodTranslator(sqlExpressionFactory),
+ new SqliteByteArrayMethodTranslator(sqlExpressionFactory, dependencies.RelationalTypeMappingSource),
new SqliteCharMethodTranslator(sqlExpressionFactory),
new SqliteDateTimeAddTranslator(sqlExpressionFactory),
new SqliteGlobMethodTranslator(sqlExpressionFactory),
diff --git a/src/EFCore.Sqlite.Core/Query/Internal/SqliteSqlTranslatingExpressionVisitor.cs b/src/EFCore.Sqlite.Core/Query/Internal/SqliteSqlTranslatingExpressionVisitor.cs
index eeb239a8bcc..5a404f753cd 100644
--- a/src/EFCore.Sqlite.Core/Query/Internal/SqliteSqlTranslatingExpressionVisitor.cs
+++ b/src/EFCore.Sqlite.Core/Query/Internal/SqliteSqlTranslatingExpressionVisitor.cs
@@ -151,6 +151,40 @@ protected override Expression VisitBinary(BinaryExpression binaryExpression)
{
Check.NotNull(binaryExpression, nameof(binaryExpression));
+ // See issue#16428
+ //if (binaryExpression.NodeType == ExpressionType.ArrayIndex
+ // && binaryExpression.Left.Type == typeof(byte[]))
+ //{
+ // var left = Visit(binaryExpression.Left);
+ // var right = Visit(binaryExpression.Right);
+
+ // if (left is SqlExpression leftSql
+ // && right is SqlExpression rightSql)
+ // {
+ // return Dependencies.SqlExpressionFactory.Function(
+ // "unicode",
+ // new SqlExpression[]
+ // {
+ // Dependencies.SqlExpressionFactory.Function(
+ // "substr",
+ // new SqlExpression[]
+ // {
+ // leftSql,
+ // Dependencies.SqlExpressionFactory.Add(
+ // Dependencies.SqlExpressionFactory.ApplyDefaultTypeMapping(rightSql),
+ // Dependencies.SqlExpressionFactory.Constant(1)),
+ // Dependencies.SqlExpressionFactory.Constant(1)
+ // },
+ // nullable: true,
+ // argumentsPropagateNullability: new[] { true, true, true },
+ // typeof(byte[]))
+ // },
+ // nullable: true,
+ // argumentsPropagateNullability: new[] { true },
+ // binaryExpression.Type);
+ // }
+ //}
+
if (!(base.VisitBinary(binaryExpression) is SqlExpression visitedExpression))
{
return QueryCompilationContext.NotTranslatedExpression;
diff --git a/test/EFCore.Specification.Tests/MonsterFixupTestBase.cs b/test/EFCore.Specification.Tests/MonsterFixupTestBase.cs
index a93f88c6629..d7da06b1743 100644
--- a/test/EFCore.Specification.Tests/MonsterFixupTestBase.cs
+++ b/test/EFCore.Specification.Tests/MonsterFixupTestBase.cs
@@ -519,11 +519,17 @@ public virtual void Composite_fixup_happens_when_FKs_change_test()
var productReview2 = context.ProductReviews.Single(e => e.Review.StartsWith("Good"));
var productReview3 = context.ProductReviews.Single(e => e.Review.StartsWith("Eeky"));
- // Issue #16428
- var productPhotos = context.ProductPhotos.ToList();
- var productPhoto1 = productPhotos.Single(e => e.Photo[0] == 101);
- var productPhoto2 = productPhotos.Single(e => e.Photo[0] == 103);
- var productPhoto3 = productPhotos.Single(e => e.Photo[0] == 105);
+ // See issue#16428
+ var sqlite = context.Database.ProviderName == "Microsoft.EntityFrameworkCore.Sqlite";
+ var productPhoto1 = sqlite
+ ? context.ProductPhotos.ToList().Single(e => e.Photo[0] == 101)
+ : context.ProductPhotos.Single(e => e.Photo[0] == 101);
+ var productPhoto2 = sqlite
+ ? context.ProductPhotos.ToList().Single(e => e.Photo[0] == 103)
+ : context.ProductPhotos.Single(e => e.Photo[0] == 103);
+ var productPhoto3 = sqlite
+ ? context.ProductPhotos.ToList().Single(e => e.Photo[0] == 105)
+ : context.ProductPhotos.Single(e => e.Photo[0] == 105);
var productWebFeature1 = context.ProductWebFeatures.Single(e => e.Heading.StartsWith("Waffle"));
var productWebFeature2 = context.ProductWebFeatures.Single(e => e.Heading.StartsWith("What"));
@@ -834,10 +840,19 @@ protected void SimpleVerification()
new[] { "Better than Tarqies!", "Eeky says yes!", "Good with maple syrup." },
context.ProductReviews.Select(c => c.Review).OrderBy(n => n));
- // Issue #16428
- Assert.Equal(
- new[] { "101", "103", "105" },
- context.ProductPhotos.ToList().Select(c => c.Photo.First().ToString()).OrderBy(n => n));
+ // See issue#16428
+ if (context.Database.ProviderName == "Microsoft.EntityFrameworkCore.Sqlite")
+ {
+ Assert.Equal(
+ new[] { "101", "103", "105" },
+ context.ProductPhotos.ToList().Select(c => c.Photo.First().ToString()).OrderBy(n => n));
+ }
+ else
+ {
+ Assert.Equal(
+ new[] { "101", "103", "105" },
+ context.ProductPhotos.Select(c => c.Photo.First().ToString()).OrderBy(n => n));
+ }
Assert.Equal(
new[] { "Waffle Style", "What does the waffle say?" },
@@ -847,7 +862,6 @@ protected void SimpleVerification()
new[] { "Ants By Boris", "Trading As Trent" },
context.Suppliers.Select(c => c.Name).OrderBy(n => n));
- // Issue #16428
Assert.Equal(
new[] { "201", "202" },
context.SupplierLogos.ToList().SelectMany(c => c.Logo).Select(l => l.ToString()).OrderBy(n => n));
@@ -1027,11 +1041,17 @@ protected void FkVerification()
Assert.Equal(product1.ProductId, productReview2.ProductId);
Assert.Equal(product2.ProductId, productReview3.ProductId);
- // Issue #16428
- var productPhotos = context.ProductPhotos.ToList();
- var productPhoto1 = productPhotos.Single(e => e.Photo[0] == 101);
- var productPhoto2 = productPhotos.Single(e => e.Photo[0] == 103);
- var productPhoto3 = productPhotos.Single(e => e.Photo[0] == 105);
+ // See issue#16428
+ var sqlite = context.Database.ProviderName == "Microsoft.EntityFrameworkCore.Sqlite";
+ var productPhoto1 = sqlite
+ ? context.ProductPhotos.ToList().Single(e => e.Photo[0] == 101)
+ : context.ProductPhotos.Single(e => e.Photo[0] == 101);
+ var productPhoto2 = sqlite
+ ? context.ProductPhotos.ToList().Single(e => e.Photo[0] == 103)
+ : context.ProductPhotos.Single(e => e.Photo[0] == 103);
+ var productPhoto3 = sqlite
+ ? context.ProductPhotos.ToList().Single(e => e.Photo[0] == 105)
+ : context.ProductPhotos.Single(e => e.Photo[0] == 105);
Assert.Equal(product1.ProductId, productPhoto1.ProductId);
Assert.Equal(product1.ProductId, productPhoto2.ProductId);
@@ -1050,8 +1070,9 @@ protected void FkVerification()
var supplier1 = context.Suppliers.Single(e => e.Name.StartsWith("Trading"));
var supplier2 = context.Suppliers.Single(e => e.Name.StartsWith("Ants"));
- // Issue #16428
- var supplierLogo1 = context.SupplierLogos.ToList().Single(e => e.Logo[0] == 201);
+ var supplierLogo1 = sqlite
+ ? context.SupplierLogos.ToList().Single(e => e.Logo[0] == 201)
+ : context.SupplierLogos.Single(e => e.Logo[0] == 201);
Assert.Equal(supplier1.SupplierId, supplierLogo1.SupplierId);
@@ -1293,11 +1314,17 @@ protected void NavigationVerification()
Assert.True(product3.Reviews == null || product3.Reviews.Count == 0);
- // Issue #16428
- var productPhotos = context.ProductPhotos.ToList();
- var productPhoto1 = productPhotos.Single(e => e.Photo[0] == 101);
- var productPhoto2 = productPhotos.Single(e => e.Photo[0] == 103);
- var productPhoto3 = productPhotos.Single(e => e.Photo[0] == 105);
+ // See issue#16428
+ var sqlite = context.Database.ProviderName == "Microsoft.EntityFrameworkCore.Sqlite";
+ var productPhoto1 = sqlite
+ ? context.ProductPhotos.ToList().Single(e => e.Photo[0] == 101)
+ : context.ProductPhotos.Single(e => e.Photo[0] == 101);
+ var productPhoto2 = sqlite
+ ? context.ProductPhotos.ToList().Single(e => e.Photo[0] == 103)
+ : context.ProductPhotos.Single(e => e.Photo[0] == 103);
+ var productPhoto3 = sqlite
+ ? context.ProductPhotos.ToList().Single(e => e.Photo[0] == 105)
+ : context.ProductPhotos.Single(e => e.Photo[0] == 105);
Assert.Equal(
new[] { productPhoto1, productPhoto2 },
@@ -1327,8 +1354,9 @@ protected void NavigationVerification()
var supplier1 = context.Suppliers.Single(e => e.Name.StartsWith("Trading"));
var supplier2 = context.Suppliers.Single(e => e.Name.StartsWith("Ants"));
- // Issue #16428
- var supplierLogo1 = context.SupplierLogos.ToList().Single(e => e.Logo[0] == 201);
+ var supplierLogo1 = sqlite
+ ? context.SupplierLogos.ToList().Single(e => e.Logo[0] == 201)
+ : context.SupplierLogos.Single(e => e.Logo[0] == 201);
Assert.Same(supplierLogo1, supplier1.Logo);