diff --git a/src/EFCore.SqlServer/Query/Internal/SqlServerQueryStringFactory.cs b/src/EFCore.SqlServer/Query/Internal/SqlServerQueryStringFactory.cs
index 4ce8381fea8..6b6e38bc2c7 100644
--- a/src/EFCore.SqlServer/Query/Internal/SqlServerQueryStringFactory.cs
+++ b/src/EFCore.SqlServer/Query/Internal/SqlServerQueryStringFactory.cs
@@ -25,7 +25,12 @@ public class SqlServerQueryStringFactory : IRelationalQueryStringFactory
{
private readonly IRelationalTypeMappingSource _typeMapper;
- /// Initializes a new instance of the class.
+ ///
+ /// 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.
+ ///
public SqlServerQueryStringFactory([NotNull] IRelationalTypeMappingSource typeMapper)
{
_typeMapper = typeMapper;
diff --git a/src/EFCore.Sqlite.Core/Extensions/SqliteServiceCollectionExtensions.cs b/src/EFCore.Sqlite.Core/Extensions/SqliteServiceCollectionExtensions.cs
index b81ba55c461..f3b405259ff 100644
--- a/src/EFCore.Sqlite.Core/Extensions/SqliteServiceCollectionExtensions.cs
+++ b/src/EFCore.Sqlite.Core/Extensions/SqliteServiceCollectionExtensions.cs
@@ -63,6 +63,7 @@ public static IServiceCollection AddEntityFrameworkSqlite([NotNull] this IServic
.TryAdd()
.TryAdd()
.TryAdd()
+ .TryAdd()
// New Query Pipeline
.TryAdd()
diff --git a/src/EFCore.Sqlite.Core/Query/Internal/SqliteQueryStringFactory.cs b/src/EFCore.Sqlite.Core/Query/Internal/SqliteQueryStringFactory.cs
new file mode 100644
index 00000000000..45a8cf8f44c
--- /dev/null
+++ b/src/EFCore.Sqlite.Core/Query/Internal/SqliteQueryStringFactory.cs
@@ -0,0 +1,67 @@
+// 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.Data.Common;
+using System.Text;
+using JetBrains.Annotations;
+using Microsoft.EntityFrameworkCore.Query;
+using Microsoft.EntityFrameworkCore.Storage;
+
+namespace Microsoft.EntityFrameworkCore.Sqlite.Query.Internal
+{
+ ///
+ /// 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.
+ ///
+ public class SqliteQueryStringFactory : IRelationalQueryStringFactory
+ {
+ private readonly IRelationalTypeMappingSource _typeMapper;
+
+ ///
+ /// 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.
+ ///
+ public SqliteQueryStringFactory([NotNull] IRelationalTypeMappingSource typeMapper)
+ {
+ _typeMapper = typeMapper;
+ }
+
+ ///
+ /// 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.
+ ///
+ public virtual string Create(DbCommand command)
+ {
+ if (command.Parameters.Count == 0)
+ {
+ return command.CommandText;
+ }
+
+ var builder = new StringBuilder();
+ foreach (DbParameter parameter in command.Parameters)
+ {
+ var value = parameter.Value;
+ builder
+ .Append(".param set ")
+ .Append(parameter.ParameterName)
+ .Append(' ')
+ .AppendLine(
+ value == null || value == DBNull.Value
+ ? "NULL"
+ : _typeMapper.FindMapping(value.GetType())?.GenerateSqlLiteral(value)
+ ?? value.ToString());
+ }
+
+ return builder
+ .AppendLine()
+ .Append(command.CommandText).ToString();
+ }
+ }
+}
diff --git a/test/EFCore.Sqlite.FunctionalTests/Query/FromSqlQuerySqliteTest.cs b/test/EFCore.Sqlite.FunctionalTests/Query/FromSqlQuerySqliteTest.cs
index 31b64c2b4d1..fd722aaf7fb 100644
--- a/test/EFCore.Sqlite.FunctionalTests/Query/FromSqlQuerySqliteTest.cs
+++ b/test/EFCore.Sqlite.FunctionalTests/Query/FromSqlQuerySqliteTest.cs
@@ -4,6 +4,7 @@
using System.Data.Common;
using Microsoft.Data.Sqlite;
using Microsoft.EntityFrameworkCore.TestUtilities;
+using Xunit;
using Xunit.Abstractions;
namespace Microsoft.EntityFrameworkCore.Query
@@ -16,6 +17,37 @@ public FromSqlQuerySqliteTest(NorthwindQuerySqliteFixture f
fixture.TestSqlLoggerFactory.SetTestOutputHelper(testOutputHelper);
}
+ public override string FromSqlRaw_queryable_composed()
+ {
+ var queryString = base.FromSqlRaw_queryable_composed();
+
+ var expected = @"SELECT ""c"".""CustomerID"", ""c"".""Address"", ""c"".""City"", ""c"".""CompanyName"", ""c"".""ContactName"", ""c"".""ContactTitle"", ""c"".""Country"", ""c"".""Fax"", ""c"".""Phone"", ""c"".""PostalCode"", ""c"".""Region""
+FROM (
+ SELECT * FROM ""Customers""
+) AS ""c""
+WHERE ('z' = '') OR (instr(""c"".""ContactName"", 'z') > 0)";
+
+ Assert.Equal(expected, queryString);
+
+ return null;
+ }
+
+ public override string FromSqlRaw_queryable_with_parameters_and_closure()
+ {
+ var queryString = base.FromSqlRaw_queryable_with_parameters_and_closure();
+
+ Assert.Equal(@".param set p0 'London'
+.param set @__contactTitle_1 'Sales Representative'
+
+SELECT ""c"".""CustomerID"", ""c"".""Address"", ""c"".""City"", ""c"".""CompanyName"", ""c"".""ContactName"", ""c"".""ContactTitle"", ""c"".""Country"", ""c"".""Fax"", ""c"".""Phone"", ""c"".""PostalCode"", ""c"".""Region""
+FROM (
+ SELECT * FROM ""Customers"" WHERE ""City"" = @p0
+) AS ""c""
+WHERE ""c"".""ContactTitle"" = @__contactTitle_1", queryString);
+
+ return null;
+ }
+
public override void Bad_data_error_handling_invalid_cast_key()
{
// Not supported on SQLite
diff --git a/test/EFCore.Sqlite.FunctionalTests/Query/NorthwindWhereQuerySqliteTest.cs b/test/EFCore.Sqlite.FunctionalTests/Query/NorthwindWhereQuerySqliteTest.cs
index 189df6c98fe..5961d7d6b89 100644
--- a/test/EFCore.Sqlite.FunctionalTests/Query/NorthwindWhereQuerySqliteTest.cs
+++ b/test/EFCore.Sqlite.FunctionalTests/Query/NorthwindWhereQuerySqliteTest.cs
@@ -36,7 +36,8 @@ public override async Task Where_simple_closure(bool async)
WHERE ""c"".""City"" = @__city_0");
Assert.Equal(
- @"-- @__city_0='London' (Size = 6)
+ @".param set @__city_0 'London'
+
SELECT ""c"".""CustomerID"", ""c"".""Address"", ""c"".""City"", ""c"".""CompanyName"", ""c"".""ContactName"", ""c"".""ContactTitle"", ""c"".""Country"", ""c"".""Fax"", ""c"".""Phone"", ""c"".""PostalCode"", ""c"".""Region""
FROM ""Customers"" AS ""c""
WHERE ""c"".""City"" = @__city_0", queryString, ignoreLineEndingDifferences: true, ignoreWhiteSpaceDifferences: true);