Skip to content

Fix parameter caching for Linq provider #2519

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Sep 6, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions src/NHibernate.Test/Async/Linq/ConstantTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
namespace NHibernate.Test.Linq
{
using System.Threading.Tasks;
using System.Threading;
// Mainly adapted from tests contributed by Nicola Tuveri on NH-2500 (NH-2500.patch file)
[TestFixture]
public class ConstantTestAsync : LinqTestCase
Expand Down Expand Up @@ -118,6 +119,29 @@ public async Task ConstantNonCachedInMemberInitExpressionAsync()
Assert.That(s2, Has.All.Property("Name").EqualTo("shipper2"), "s2 Names");
}

[Test]
public async Task ConstantNonCachedInMemberInitExpressionWithConditionAsync()
{
var shipper1 = await (GetShipperAsync(1));
var shipper2 = await (GetShipperAsync(2));

Assert.That(shipper1.Number, Is.EqualTo(1));
Assert.That(shipper2.Number, Is.EqualTo(2));
}

private Task<ShipperDto> GetShipperAsync(int id, CancellationToken cancellationToken = default(CancellationToken))
{
try
{
return db.Shippers.Where(o => o.ShipperId == id)
.Select(o => new ShipperDto {Number = id, CompanyName = o.CompanyName}).SingleAsync(cancellationToken);
}
catch (System.Exception ex)
{
return Task.FromException<ShipperDto>(ex);
}
}

[Test]
public async Task ConstantInNewArrayExpressionAsync()
{
Expand Down
16 changes: 16 additions & 0 deletions src/NHibernate.Test/Linq/ConstantTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,22 @@ public void ConstantNonCachedInMemberInitExpression()
Assert.That(s2, Has.All.Property("Name").EqualTo("shipper2"), "s2 Names");
}

[Test]
public void ConstantNonCachedInMemberInitExpressionWithCondition()
{
var shipper1 = GetShipper(1);
var shipper2 = GetShipper(2);

Assert.That(shipper1.Number, Is.EqualTo(1));
Assert.That(shipper2.Number, Is.EqualTo(2));
}

private ShipperDto GetShipper(int id)
{
return db.Shippers.Where(o => o.ShipperId == id)
.Select(o => new ShipperDto {Number = id, CompanyName = o.CompanyName}).Single();
}

[Test]
public void ConstantInNewArrayExpression()
{
Expand Down
2 changes: 1 addition & 1 deletion src/NHibernate/Linq/NhLinqExpression.cs
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ public IASTNode Translate(ISessionFactoryImplementor sessionFactory, bool filter

ParameterDescriptors = requiredHqlParameters.AsReadOnly();

CanCachePlan = CanCachePlan &&
CanCachePlan = CanCachePlan && visitorParameters.CanCachePlan &&
// If some constants do not have matching HQL parameters, their values from first query will
// be embedded in the plan and reused for subsequent queries: do not cache the plan.
!ParameterValuesByName
Expand Down
8 changes: 8 additions & 0 deletions src/NHibernate/Linq/Visitors/SelectClauseNominator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using NHibernate.Engine;
using NHibernate.Linq.Functions;
using NHibernate.Linq.Expressions;
using NHibernate.Param;
using NHibernate.Util;
using Remotion.Linq.Parsing;

Expand All @@ -17,6 +18,7 @@ class SelectClauseHqlNominator : RelinqExpressionVisitor
{
private readonly ILinqToHqlGeneratorsRegistry _functionRegistry;
private readonly ISessionFactoryImplementor _sessionFactory;
private readonly VisitorParameters _parameters;

/// <summary>
/// The expression parts that can be converted to pure HQL.
Expand All @@ -38,6 +40,7 @@ public SelectClauseHqlNominator(VisitorParameters parameters)
{
_functionRegistry = parameters.SessionFactory.Settings.LinqToHqlGeneratorsRegistry;
_sessionFactory = parameters.SessionFactory;
_parameters = parameters;
}

internal Expression Nominate(Expression expression)
Expand Down Expand Up @@ -152,6 +155,11 @@ private bool CanBeEvaluatedInHqlSelectStatement(Expression expression, bool proj
// Constants will only be evaluated in HQL if they're inside a method call
if (expression.NodeType == ExpressionType.Constant)
{
if (!projectConstantsInHql && _parameters.ConstantToParameterMap.ContainsKey((ConstantExpression)expression))
{
_parameters.CanCachePlan = false;
}

return projectConstantsInHql;
}

Expand Down
4 changes: 3 additions & 1 deletion src/NHibernate/Linq/Visitors/VisitorParameters.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ public class VisitorParameters

public QueryMode RootQueryMode { get; }

internal bool CanCachePlan { get; set; } = true;

public VisitorParameters(
ISessionFactoryImplementor sessionFactory,
IDictionary<ConstantExpression, NamedParameter> constantToParameterMap,
Expand All @@ -39,4 +41,4 @@ public VisitorParameters(
RootQueryMode = rootQueryMode;
}
}
}
}