Skip to content

Commit b340c00

Browse files
committed
feat: UseDeferredEnumeration option
1 parent 9bf7bbd commit b340c00

File tree

2 files changed

+31
-5
lines changed

2 files changed

+31
-5
lines changed

src/QueryableValues.SqlServer/QueryableValuesSqlServerOptions.cs

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,15 @@
1-
namespace BlazarTech.QueryableValues
1+
using System;
2+
using System.Collections.Generic;
3+
4+
namespace BlazarTech.QueryableValues
25
{
36
/// <summary>
47
/// QueryableValues options for SQL Server.
58
/// </summary>
69
public sealed class QueryableValuesSqlServerOptions
710
{
811
internal bool WithUseSelectTopOptimization { get; private set; } = true;
12+
internal bool WithUseDeferredEnumeration { get; private set; } = true;
913

1014
/// <summary>
1115
/// When possible, uses a <c>TOP(n)</c> clause in the underlying <c>SELECT</c> statement to assist SQL Server memory grant estimation. The default is <see langword="true"/>.
@@ -21,5 +25,29 @@ public QueryableValuesSqlServerOptions UseSelectTopOptimization(bool useSelectTo
2125
WithUseSelectTopOptimization = useSelectTopOptimization;
2226
return this;
2327
}
28+
29+
#if !EFCORE3
30+
/// <summary>
31+
/// If <see langword="true"/>, the <see cref="IEnumerable{T}"/> provided to any of the <c>AsQueryableValues</c> methods will be enumerated when the query is materialized; otherwise, it will be immediately enumerated once. The default is <see langword="true"/>.
32+
/// </summary>
33+
/// <remarks>
34+
/// <para>
35+
/// Leaving this feature enabled has the following advantages:<br/>
36+
/// - If your sequence of values is behind an <see cref="IEnumerable{T}"/>, it will be enumerated only when needed.<br/>
37+
/// - The sequence is enumerated every time the query is materialized, allowing the query to be aware of any changes done to the underlying sequence.
38+
/// </para>
39+
/// <para>
40+
/// You may want to disable this feature if you rely on the <see cref="Microsoft.EntityFrameworkCore.EntityFrameworkQueryableExtensions.ToQueryString(System.Linq.IQueryable)"/> method across your application.
41+
/// As of EF 7.0, the implementation of that API is making incompatible assumptions about the underlying ADO.NET query parameters, resulting in an <see cref="InvalidCastException"/> when this option is enabled.
42+
/// </para>
43+
/// </remarks>
44+
/// <param name="useDeferredEnumeration"></param>
45+
/// <returns>The same <see cref="QueryableValuesSqlServerOptions"/> instance so subsequent configurations can be chained.</returns>
46+
public QueryableValuesSqlServerOptions UseDeferredEnumeration(bool useDeferredEnumeration = true)
47+
{
48+
WithUseDeferredEnumeration = useDeferredEnumeration;
49+
return this;
50+
}
51+
#endif
2452
}
2553
}

src/QueryableValues.SqlServer/SqlServer/XmlQueryableFactory.cs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -82,17 +82,15 @@ private SqlParameter[] GetSqlParameters<T>(DeferredValues<T> deferredValues)
8282
var xmlParameter = new SqlParameter(null, SqlDbType.Xml)
8383
{
8484
// DeferredValues allows us to defer the enumeration of values until the query is materialized.
85-
// Uses deferredValues.ToString() at evaluation time.
86-
Value = deferredValues
85+
Value = _options.WithUseDeferredEnumeration ? deferredValues : deferredValues.ToString(null)
8786
};
8887

8988
if (UseSelectTopOptimization(deferredValues))
9089
{
9190
// bigint to avoid implicit casting by the TOP operation (observed in the execution plan).
9291
var countParameter = new SqlParameter(null, SqlDbType.BigInt)
9392
{
94-
// Uses deferredValues.ToInt64() at evaluation time.
95-
Value = deferredValues
93+
Value = _options.WithUseDeferredEnumeration ? deferredValues : deferredValues.ToInt64(null)
9694
};
9795

9896
sqlParameters = new[] { xmlParameter, countParameter };

0 commit comments

Comments
 (0)