diff --git a/src/EFCore.Relational/Storage/Internal/RawRelationalParameter.cs b/src/EFCore.Relational/Storage/Internal/RawRelationalParameter.cs index 5e0cb1a2f0e..d10a141dba0 100644 --- a/src/EFCore.Relational/Storage/Internal/RawRelationalParameter.cs +++ b/src/EFCore.Relational/Storage/Internal/RawRelationalParameter.cs @@ -1,7 +1,9 @@ // 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; using System.Data.Common; using JetBrains.Annotations; using Microsoft.EntityFrameworkCore.Utilities; @@ -62,6 +64,13 @@ public override void AddDbParameter(DbCommand command, IReadOnlyDictionary public override void AddDbParameter(DbCommand command, object value) { + if (value is DbParameter dbParameter + && dbParameter.Direction == ParameterDirection.Input + && value is ICloneable cloneable) + { + value = cloneable.Clone(); + } + command.Parameters.Add(value); } } diff --git a/test/EFCore.Relational.Specification.Tests/Query/FromSqlQueryTestBase.cs b/test/EFCore.Relational.Specification.Tests/Query/FromSqlQueryTestBase.cs index df0f2e7b1e2..8f3d6892d09 100644 --- a/test/EFCore.Relational.Specification.Tests/Query/FromSqlQueryTestBase.cs +++ b/test/EFCore.Relational.Specification.Tests/Query/FromSqlQueryTestBase.cs @@ -987,6 +987,24 @@ public virtual void Line_endings_after_Select() Assert.NotNull(actual); } + [ConditionalFact] + public virtual void FromSql_with_db_parameter_in_split_query() + { + using var context = CreateContext(); + + var actual = context.Set() + .FromSqlRaw(NormalizeDelimitersInRawString("SELECT * FROM [Customers] WHERE [CustomerID] = {0}"), + CreateDbParameter("customerID", "ALFKI")) + .Include(e => e.Orders) + .ThenInclude(o => o.OrderDetails) + .AsSplitQuery() + .ToArray(); + + var customer = Assert.Single(actual); + Assert.Equal(6, customer.Orders.Count); + Assert.Equal(12, customer.Orders.SelectMany(e => e.OrderDetails).Count()); + } + protected string NormalizeDelimitersInRawString(string sql) => Fixture.TestStore.NormalizeDelimitersInRawString(sql); diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/FromSqlQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/FromSqlQuerySqlServerTest.cs index c550dc1daf6..a903eb84a00 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/FromSqlQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/FromSqlQuerySqlServerTest.cs @@ -799,6 +799,39 @@ public override void Line_endings_after_Select() WHERE [c].[City] = N'Seattle'"); } + public override void FromSql_with_db_parameter_in_split_query() + { + base.FromSql_with_db_parameter_in_split_query(); + + AssertSql( + @"customerID='ALFKI' (Nullable = false) (Size = 5) + +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 ""CustomerID"" = @customerID +) AS [c] +ORDER BY [c].[CustomerID]", + // + @"customerID='ALFKI' (Nullable = false) (Size = 5) + +SELECT [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate], [c].[CustomerID] +FROM ( + SELECT * FROM ""Customers"" WHERE ""CustomerID"" = @customerID +) AS [c] +INNER JOIN [Orders] AS [o] ON [c].[CustomerID] = [o].[CustomerID] +ORDER BY [c].[CustomerID], [o].[OrderID]", + // + @"customerID='ALFKI' (Nullable = false) (Size = 5) + +SELECT [o0].[OrderID], [o0].[ProductID], [o0].[Discount], [o0].[Quantity], [o0].[UnitPrice], [c].[CustomerID], [o].[OrderID] +FROM ( + SELECT * FROM ""Customers"" WHERE ""CustomerID"" = @customerID +) AS [c] +INNER JOIN [Orders] AS [o] ON [c].[CustomerID] = [o].[CustomerID] +INNER JOIN [Order Details] AS [o0] ON [o].[OrderID] = [o0].[OrderID] +ORDER BY [c].[CustomerID], [o].[OrderID]"); + } + protected override DbParameter CreateDbParameter(string name, object value) => new SqlParameter { ParameterName = name, Value = value };