Skip to content

Commit e5bf791

Browse files
authored
Merge pull request #26 from yv989c/develop
UseDeferredEnumeration
2 parents aa815e9 + 512109e commit e5bf791

File tree

8 files changed

+51
-32
lines changed

8 files changed

+51
-32
lines changed

README.md

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -38,15 +38,6 @@ Your ⭐ on [this repository][Repository] also helps! Thanks! 🖖🙂
3838
## Installation
3939
QueryableValues is distributed as a [NuGet Package]. The major version number of this library is aligned with the version of [Entity Framework Core] by which it's supported (e.g. If you are using EF Core 5, then you must use version 5 of QueryableValues).
4040

41-
Please choose the appropriate command below to install it using the NuGet Package Manager Console window in Visual Studio:
42-
43-
EF Core | Command
44-
:---: | ---
45-
3.x | `Install-Package BlazarTech.QueryableValues.SqlServer -Version 3.5.0`
46-
5.x | `Install-Package BlazarTech.QueryableValues.SqlServer -Version 5.5.0`
47-
6.x | `Install-Package BlazarTech.QueryableValues.SqlServer -Version 6.5.0`
48-
7.x | `Install-Package BlazarTech.QueryableValues.SqlServer -Version 7.0.0`
49-
5041
## Configuration
5142
Look for the place in your code where you are setting up your [DbContext] and calling the [UseSqlServer] extension method, then use a lambda expression to access the `SqlServerDbContextOptionsBuilder` provided by it. It is on this builder that you must call the `UseQueryableValues` extension method as shown in the following simplified examples:
5243

Version.xml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
<Project>
22
<PropertyGroup>
3-
<VersionEFCore3>3.5.0</VersionEFCore3>
4-
<VersionEFCore5>5.5.0</VersionEFCore5>
5-
<VersionEFCore6>6.5.0</VersionEFCore6>
6-
<VersionEFCore7>7.0.0</VersionEFCore7>
3+
<VersionEFCore3>3.6.0</VersionEFCore3>
4+
<VersionEFCore5>5.6.0</VersionEFCore5>
5+
<VersionEFCore6>6.6.0</VersionEFCore6>
6+
<VersionEFCore7>7.1.0</VersionEFCore7>
77
</PropertyGroup>
88
</Project>

docs/README.md

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -34,15 +34,6 @@ Your ⭐ on [this repository][Repository] also helps! Thanks! 🖖🙂
3434
## Installation
3535
QueryableValues is distributed as a [NuGet Package]. The major version number of this library is aligned with the version of [Entity Framework Core] by which it's supported (e.g. If you are using EF Core 5, then you must use version 5 of QueryableValues).
3636

37-
Please choose the appropriate command below to install it using the NuGet Package Manager Console window in Visual Studio:
38-
39-
EF Core | Command
40-
:---: | ---
41-
3.x | `Install-Package BlazarTech.QueryableValues.SqlServer -Version 3.5.0`
42-
5.x | `Install-Package BlazarTech.QueryableValues.SqlServer -Version 5.5.0`
43-
6.x | `Install-Package BlazarTech.QueryableValues.SqlServer -Version 6.5.0`
44-
7.x | `Install-Package BlazarTech.QueryableValues.SqlServer -Version 7.0.0`
45-
4637
## Configuration
4738
Look for the place in your code where you are setting up your [DbContext] and calling the [UseSqlServer] extension method, then use a lambda expression to access the `SqlServerDbContextOptionsBuilder` provided by it. It is on this builder that you must call the `UseQueryableValues` extension method as shown in the following simplified examples:
4839

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 };

tests/QueryableValues.SqlServer.Tests.EFCore3/QueryableValues.SqlServer.Tests.EFCore3.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
<Import Project="../SharedTestProjectProperties.xml" />
33

44
<PropertyGroup>
5-
<TargetFrameworks>netcoreapp3.1;net5.0;net6.0</TargetFrameworks>
5+
<TargetFrameworks>netcoreapp3.1;net6.0</TargetFrameworks>
66
<AssemblyName>BlazarTech.QueryableValues.SqlServer.Tests.EFCore3</AssemblyName>
77
<DefineConstants>$(DefineConstants);TESTS;EFCORE3</DefineConstants>
88
</PropertyGroup>

tests/QueryableValues.SqlServer.Tests.EFCore5/QueryableValues.SqlServer.Tests.EFCore5.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
<Import Project="../SharedTestProjectProperties.xml" />
33

44
<PropertyGroup>
5-
<TargetFrameworks>netcoreapp3.1;net5.0;net6.0</TargetFrameworks>
5+
<TargetFrameworks>netcoreapp3.1;net6.0</TargetFrameworks>
66
<AssemblyName>BlazarTech.QueryableValues.SqlServer.Tests.EFCore5</AssemblyName>
77
<DefineConstants>$(DefineConstants);TESTS;EFCORE5</DefineConstants>
88
</PropertyGroup>

tests/QueryableValues.SqlServer.Tests/Integration/MyDbContextBase.cs

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ public abstract class MyDbContextBase : DbContext
1010
private readonly string _databaseName;
1111
private readonly bool _useQueryableValues;
1212
private readonly bool _useSelectTopOptimization;
13+
private readonly bool _useUseDeferredEnumeration;
1314

1415
#if !EFCORE3
1516
public event Action<string>? LogEntryEmitted;
@@ -20,12 +21,14 @@ public abstract class MyDbContextBase : DbContext
2021
public MyDbContextBase(
2122
string databaseName,
2223
bool useQueryableValues = true,
23-
bool useSelectTopOptimization = true
24+
bool useSelectTopOptimization = true,
25+
bool useUseDeferredEnumeration = true
2426
)
2527
{
2628
_databaseName = databaseName;
2729
_useQueryableValues = useQueryableValues;
2830
_useSelectTopOptimization = useSelectTopOptimization;
31+
_useUseDeferredEnumeration = useUseDeferredEnumeration;
2932
}
3033

3134
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
@@ -34,7 +37,8 @@ protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
3437

3538
#if !EFCORE3
3639
optionsBuilder.LogTo(
37-
logEntry => {
40+
logEntry =>
41+
{
3842
LogEntryEmitted?.Invoke(logEntry);
3943
},
4044
Microsoft.Extensions.Logging.LogLevel.Information);
@@ -46,7 +50,7 @@ protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
4650
{
4751
if (_useQueryableValues)
4852
{
49-
var applyOptions = !_useSelectTopOptimization;
53+
var applyOptions = !_useSelectTopOptimization || !_useUseDeferredEnumeration;
5054

5155
if (applyOptions)
5256
{
@@ -56,6 +60,13 @@ protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
5660
{
5761
options.UseSelectTopOptimization(false);
5862
}
63+
64+
#if !EFCORE3
65+
if (!_useUseDeferredEnumeration)
66+
{
67+
options.UseDeferredEnumeration(false);
68+
}
69+
#endif
5970
});
6071
}
6172
else

0 commit comments

Comments
 (0)