Skip to content

Commit 9c97700

Browse files
authored
Merge pull request #69 from DataObjects-NET/include-provider-bugfix
Fix the issue with the multiple In statements in the same query
2 parents 9544eb4 + 00ce403 commit 9c97700

4 files changed

Lines changed: 106 additions & 24 deletions

File tree

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
// Copyright (C) 2020 Xtensive LLC.
2+
// This code is distributed under MIT license terms.
3+
// See the License.txt file in the project root for more information.
4+
5+
using System;
6+
using System.Linq;
7+
using System.Reflection;
8+
using NUnit.Framework;
9+
using Xtensive.Orm.Configuration;
10+
using Xtensive.Orm.Tests.Issues.IssueGitHub0070_IncludeProviderParameterConflictWithinParameterContextModel;
11+
12+
namespace Xtensive.Orm.Tests.Issues.IssueGitHub0070_IncludeProviderParameterConflictWithinParameterContextModel
13+
{
14+
[HierarchyRoot]
15+
[KeyGenerator(KeyGeneratorKind.None)]
16+
public class Token: Entity
17+
{
18+
[Field, Key]
19+
public Guid Id { get; private set; }
20+
21+
[Field]
22+
public long Salt { get; set; }
23+
24+
public Token(Session session, Guid id)
25+
: base(session, id)
26+
{ }
27+
}
28+
}
29+
30+
namespace Xtensive.Orm.Tests.Issues
31+
{
32+
[TestFixture]
33+
public class IssueGitHub0070_IncludeProviderParameterConflictWithinParameterContext : AutoBuildTest
34+
{
35+
protected override DomainConfiguration BuildConfiguration()
36+
{
37+
var config = base.BuildConfiguration();
38+
config.Types.Register(typeof(Token).Assembly, typeof(Token).Namespace);
39+
return config;
40+
}
41+
42+
[Test]
43+
public void TwoDifferentInStatementsInTheSameQuery()
44+
{
45+
using (var session = Domain.OpenSession())
46+
using (session.OpenTransaction()) {
47+
var tokenId = Guid.NewGuid();
48+
var token = new Token(session, tokenId) {
49+
Salt = -1
50+
};
51+
session.SaveChanges();
52+
var query = session.Query.All<Token>()
53+
.Where(t => t.Salt.In(-1, 1) && t.Id.In(tokenId, Guid.Empty));
54+
Assert.AreSame(token, query.FirstOrDefault());
55+
}
56+
}
57+
}
58+
}

Orm/Xtensive.Orm/Core/Parameter{TValue}.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ namespace Xtensive.Core
1313
/// Parameter - an object identifying its value in active <see cref="ParameterContext"/>.
1414
/// </summary>
1515
/// <typeparam name="TValue">The type of parameter value.</typeparam>
16-
public sealed class Parameter<TValue> : Parameter
16+
public class Parameter<TValue> : Parameter
1717
{
1818
/// <summary>
1919
/// Always fails.

Orm/Xtensive.Orm/Orm/Providers/SqlCompiler.Include.cs

Lines changed: 23 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,9 @@
88
using System.Collections.Generic;
99
using System.Linq;
1010
using Xtensive.Core;
11-
using Xtensive.Collections;
1211
using Tuple = Xtensive.Tuples.Tuple;
1312
using Xtensive.Sql;
1413
using Xtensive.Sql.Dml;
15-
using Xtensive.Orm.Rse;
1614
using Xtensive.Orm.Rse.Providers;
1715

1816
namespace Xtensive.Orm.Providers
@@ -32,25 +30,29 @@ protected override SqlProvider VisitInclude(IncludeProvider provider)
3230
TemporaryTableDescriptor tableDescriptor = null;
3331
QueryParameterBinding extraBinding = null;
3432
var algorithm = provider.Algorithm;
35-
if (!temporaryTablesSupported)
33+
if (!temporaryTablesSupported) {
3634
algorithm = IncludeAlgorithm.ComplexCondition;
35+
}
36+
3737
switch (algorithm) {
3838
case IncludeAlgorithm.Auto:
39-
var complexConditionExpression = CreateIncludeViaComplexConditionExpression(
40-
provider, BuildRowFilterParameterAccessor(filterDataSource, true),
41-
sourceColumns, out extraBinding);
4239
var temporaryTableExpression = CreateIncludeViaTemporaryTableExpression(
4340
provider, sourceColumns, out tableDescriptor);
41+
var complexConditionExpression = CreateIncludeViaComplexConditionExpression(
42+
provider, BuildAutoRowFilterParameterAccessor(tableDescriptor),
43+
sourceColumns, out extraBinding);
4444
resultExpression = SqlDml.Variant(extraBinding,
4545
complexConditionExpression, temporaryTableExpression);
4646
anyTemporaryTablesRequired = true;
4747
break;
4848
case IncludeAlgorithm.ComplexCondition:
4949
resultExpression = CreateIncludeViaComplexConditionExpression(
50-
provider, BuildRowFilterParameterAccessor(filterDataSource, false),
50+
provider, BuildComplexConditionRowFilterParameterAccessor(filterDataSource),
5151
sourceColumns, out extraBinding);
52-
if (!anyTemporaryTablesRequired)
52+
if (!anyTemporaryTablesRequired) {
5353
requestOptions |= QueryRequestOptions.AllowOptimization;
54+
}
55+
5456
break;
5557
case IncludeAlgorithm.TemporaryTable:
5658
resultExpression = CreateIncludeViaTemporaryTableExpression(
@@ -63,8 +65,10 @@ protected override SqlProvider VisitInclude(IncludeProvider provider)
6365
resultExpression = GetBooleanColumnExpression(resultExpression);
6466
var calculatedColumn = provider.Header.Columns[provider.Header.Length - 1];
6567
AddInlinableColumn(provider, calculatedColumn, resultQuery, resultExpression);
66-
if (extraBinding!=null)
67-
bindings = bindings.Concat(EnumerableUtils.One(extraBinding));
68+
if (extraBinding!=null) {
69+
bindings = bindings.Append(extraBinding);
70+
}
71+
6872
var request = CreateQueryRequest(Driver, resultQuery, bindings, provider.Header.TupleDescriptor, requestOptions);
6973
return new SqlIncludeProvider(Handlers, request, tableDescriptor, filterDataSource, provider, source);
7074
}
@@ -97,16 +101,15 @@ protected SqlExpression CreateIncludeViaTemporaryTableExpression(
97101
return resultExpression;
98102
}
99103

100-
protected static Func<ParameterContext, object> BuildRowFilterParameterAccessor(
101-
Func<ParameterContext, IEnumerable<Tuple>> filterDataSource, bool takeFromContext)
102-
{
103-
if (!takeFromContext) {
104-
return context => filterDataSource.Invoke(context).ToList();
105-
}
104+
private static Func<ParameterContext, object> BuildComplexConditionRowFilterParameterAccessor(
105+
Func<ParameterContext, IEnumerable<Tuple>> filterDataSource) =>
106+
context => filterDataSource.Invoke(context).ToList();
106107

107-
return context => context.TryGetValue(SqlIncludeProvider.rowFilterParameter, out var filterData)
108-
? filterData
109-
: null;
110-
}
108+
private static Func<ParameterContext, object> BuildAutoRowFilterParameterAccessor(
109+
TemporaryTableDescriptor tableDescriptor) =>
110+
context =>
111+
context.TryGetValue(SqlIncludeProvider.CreateFilterParameter(tableDescriptor), out var filterData)
112+
? filterData
113+
: null;
111114
}
112115
}

Orm/Xtensive.Orm/Orm/Providers/SqlIncludeProvider.cs

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,32 @@ namespace Xtensive.Orm.Providers
1818
/// </summary>
1919
public sealed class SqlIncludeProvider : SqlTemporaryDataProvider
2020
{
21-
internal static readonly Parameter<List<Tuple>> rowFilterParameter = new Parameter<List<Tuple>>("RowFilterData");
21+
private class RowFilterParameter : Parameter<List<Tuple>>, IEquatable<RowFilterParameter>
22+
{
23+
private readonly TemporaryTableDescriptor temporaryTableDescriptor;
24+
25+
public bool Equals(RowFilterParameter other) =>
26+
!ReferenceEquals(null, other)
27+
&& (ReferenceEquals(this, other) || ReferenceEquals(temporaryTableDescriptor, other.temporaryTableDescriptor));
28+
29+
public override bool Equals(object obj) =>
30+
!ReferenceEquals(null, obj)
31+
&& (ReferenceEquals(this, obj) || (obj is RowFilterParameter rowFilterParameter && Equals(rowFilterParameter)));
32+
33+
public override int GetHashCode() => temporaryTableDescriptor != null ? temporaryTableDescriptor.GetHashCode() : 0;
34+
35+
public RowFilterParameter(TemporaryTableDescriptor temporaryTableDescriptor) : base("RowFilterData")
36+
{
37+
this.temporaryTableDescriptor = temporaryTableDescriptor;
38+
}
39+
}
2240

2341
private readonly Func<ParameterContext, IEnumerable<Tuple>> filterDataSource;
2442

25-
private new IncludeProvider Origin { get { return (IncludeProvider) base.Origin; } }
43+
private new IncludeProvider Origin => (IncludeProvider) base.Origin;
44+
45+
internal static Parameter<List<Tuple>> CreateFilterParameter(TemporaryTableDescriptor temporaryTableDescriptor) =>
46+
new RowFilterParameter(temporaryTableDescriptor);
2647

2748
/// <inheritdoc/>
2849
protected internal override void OnBeforeEnumerate(Rse.Providers.EnumerationContext context)
@@ -36,7 +57,7 @@ protected internal override void OnBeforeEnumerate(Rse.Providers.EnumerationCont
3657
LockAndStore(context, filterData);
3758
}
3859
else {
39-
parameterContext.SetValue(rowFilterParameter, filterData);
60+
parameterContext.SetValue(CreateFilterParameter(tableDescriptor), filterData);
4061
}
4162

4263
break;

0 commit comments

Comments
 (0)