Skip to content

Commit 74b97af

Browse files
authored
Add Linq parameter type detection (#2365)
1 parent fbf41a6 commit 74b97af

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

45 files changed

+1648
-272
lines changed
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
using System.Collections;
2+
3+
namespace NHibernate.DomainModel.Northwind.Entities
4+
{
5+
public class DynamicUser : IEnumerable
6+
{
7+
public virtual int Id { get; set; }
8+
9+
public virtual dynamic Properties { get; set; }
10+
11+
public virtual IDictionary Settings { get; set; }
12+
13+
public virtual IEnumerator GetEnumerator()
14+
{
15+
throw new System.NotImplementedException();
16+
}
17+
}
18+
}

src/NHibernate.DomainModel/Northwind/Entities/Northwind.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,11 @@ public IQueryable<User> Users
6969
get { return _session.Query<User>(); }
7070
}
7171

72+
public IQueryable<DynamicUser> DynamicUsers
73+
{
74+
get { return _session.Query<DynamicUser>(); }
75+
}
76+
7277
public IQueryable<PatientRecord> PatientRecords
7378
{
7479
get { return _session.Query<PatientRecord>(); }

src/NHibernate.DomainModel/Northwind/Entities/User.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,10 +48,16 @@ public class User : IUser, IEntity
4848

4949
public virtual FeatureSet Features { get; set; }
5050

51+
public virtual User NotMappedUser => this;
52+
5153
public virtual EnumStoredAsString Enum1 { get; set; }
5254

55+
public virtual EnumStoredAsString? NullableEnum1 { get; set; }
56+
5357
public virtual EnumStoredAsInt32 Enum2 { get; set; }
5458

59+
public virtual EnumStoredAsInt32? NullableEnum2 { get; set; }
60+
5561
public virtual IUser CreatedBy { get; set; }
5662

5763
public virtual IUser ModifiedBy { get; set; }
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<?xml version="1.0" encoding="utf-8" ?>
2+
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" namespace="NHibernate.DomainModel.Northwind.Entities" assembly="NHibernate.DomainModel">
3+
<class name="DynamicUser" mutable="false">
4+
<subselect>
5+
select * from Users
6+
</subselect>
7+
8+
<id name="Id" column="UserId" type="Int32">
9+
<generator class="assigned" />
10+
</id>
11+
12+
<dynamic-component name="Properties">
13+
<property name="Name" type="AnsiString" />
14+
<property name="Enum1" type="NHibernate.DomainModel.Northwind.Entities.EnumStoredAsStringType, NHibernate.DomainModel">
15+
<column name="Enum1" length="12" />
16+
</property>
17+
<many-to-one name="CreatedBy" class="User" not-null="true" lazy="false">
18+
<column name="CreatedById" not-null="true" />
19+
</many-to-one>
20+
</dynamic-component>
21+
22+
<dynamic-component name="Settings">
23+
<property name="Property1" type="AnsiString" />
24+
<property name="Property2" type="AnsiString" />
25+
<many-to-one name="ModifiedBy" class="User" lazy="false">
26+
<column name="ModifiedById" />
27+
</many-to-one>
28+
</dynamic-component>
29+
</class>
30+
</hibernate-mapping>

src/NHibernate.DomainModel/Northwind/Mappings/User.hbm.xml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,14 @@
2424
<column name="Enum1" length="12" />
2525
</property>
2626

27+
<property name="NullableEnum1" type="NHibernate.DomainModel.Northwind.Entities.EnumStoredAsStringType, NHibernate.DomainModel"
28+
formula="(case when Enum1 = 'Unspecified' then null else Enum1 end)" insert="false" update="false">
29+
</property>
30+
2731
<property name="Enum2" not-null="true" />
2832

33+
<property name="NullableEnum2" formula="(case when Enum2 = 0 then null else Enum2 end)" insert="false" update="false" />
34+
2935
<property name="Features" not-null="true" />
3036

3137
<many-to-one name="Role" class="Role">

src/NHibernate.Test/Async/Linq/EnumTests.cs

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,5 +61,42 @@ public async Task CanQueryOnEnumStoredAsString_Small_1Async()
6161

6262
Assert.AreEqual(expectedCount, query.Count);
6363
}
64+
65+
[Test]
66+
public async Task ConditionalNavigationPropertyAsync()
67+
{
68+
EnumStoredAsString? type = null;
69+
await (db.Users.Where(o => o.Enum1 == EnumStoredAsString.Large).ToListAsync());
70+
await (db.Users.Where(o => EnumStoredAsString.Large != o.Enum1).ToListAsync());
71+
await (db.Users.Where(o => (o.NullableEnum1 ?? EnumStoredAsString.Large) == EnumStoredAsString.Medium).ToListAsync());
72+
await (db.Users.Where(o => ((o.NullableEnum1 ?? type) ?? o.Enum1) == EnumStoredAsString.Medium).ToListAsync());
73+
74+
await (db.Users.Where(o => (o.NullableEnum1.HasValue ? o.Enum1 : EnumStoredAsString.Unspecified) == EnumStoredAsString.Medium).ToListAsync());
75+
await (db.Users.Where(o => (o.Enum1 != EnumStoredAsString.Large
76+
? (o.NullableEnum1.HasValue ? o.Enum1 : EnumStoredAsString.Unspecified)
77+
: EnumStoredAsString.Small) == EnumStoredAsString.Medium).ToListAsync());
78+
79+
await (db.Users.Where(o => (o.Enum1 == EnumStoredAsString.Large ? o.Role : o.Role).Name == "test").ToListAsync());
80+
}
81+
82+
[Test]
83+
public async Task CanQueryComplexExpressionOnEnumStoredAsStringAsync()
84+
{
85+
var type = EnumStoredAsString.Unspecified;
86+
var query = await ((from user in db.Users
87+
where (user.NullableEnum1 == EnumStoredAsString.Large
88+
? EnumStoredAsString.Medium
89+
: user.NullableEnum1 ?? user.Enum1
90+
) == type
91+
select new
92+
{
93+
user,
94+
simple = user.Enum1,
95+
condition = user.Enum1 == EnumStoredAsString.Large ? EnumStoredAsString.Medium : user.Enum1,
96+
coalesce = user.NullableEnum1 ?? EnumStoredAsString.Medium
97+
}).ToListAsync());
98+
99+
Assert.That(query.Count, Is.EqualTo(0));
100+
}
64101
}
65102
}

src/NHibernate.Test/Async/Linq/ParameterTests.cs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,34 @@ public async Task UsingTwoEntityParametersAsync()
8888
2));
8989
}
9090

91+
[Test]
92+
public async Task UsingEntityEnumerableParameterTwiceAsync()
93+
{
94+
if (!Dialect.SupportsSubSelects)
95+
{
96+
Assert.Ignore();
97+
}
98+
99+
var enumerable = await (db.DynamicUsers.FirstAsync());
100+
await (AssertTotalParametersAsync(
101+
db.DynamicUsers.Where(o => o == enumerable && o != enumerable),
102+
1));
103+
}
104+
105+
[Test]
106+
public async Task UsingEntityEnumerableListParameterTwiceAsync()
107+
{
108+
if (!Dialect.SupportsSubSelects)
109+
{
110+
Assert.Ignore();
111+
}
112+
113+
var enumerable = new[] {await (db.DynamicUsers.FirstAsync())};
114+
await (AssertTotalParametersAsync(
115+
db.DynamicUsers.Where(o => enumerable.Contains(o) && enumerable.Contains(o)),
116+
1));
117+
}
118+
91119
[Test]
92120
public async Task UsingValueTypeParameterTwiceAsync()
93121
{

src/NHibernate.Test/Linq/ConstantTest.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -217,12 +217,12 @@ public void ConstantInWhereDoesNotCauseManyKeys()
217217
select c);
218218
var preTransformParameters = new PreTransformationParameters(QueryMode.Select, Sfi);
219219
var preTransformResult = NhRelinqQueryParser.PreTransform(q1.Expression, preTransformParameters);
220-
var expression = ExpressionParameterVisitor.Visit(preTransformResult, out var parameters1);
221-
var k1 = ExpressionKeyVisitor.Visit(expression, parameters1);
220+
var parameters1 = ExpressionParameterVisitor.Visit(preTransformResult);
221+
var k1 = ExpressionKeyVisitor.Visit(preTransformResult.Expression, parameters1, Sfi);
222222

223223
var preTransformResult2 = NhRelinqQueryParser.PreTransform(q2.Expression, preTransformParameters);
224-
var expression2 = ExpressionParameterVisitor.Visit(preTransformResult2, out var parameters2);
225-
var k2 = ExpressionKeyVisitor.Visit(expression2, parameters2);
224+
var parameters2 = ExpressionParameterVisitor.Visit(preTransformResult2);
225+
var k2 = ExpressionKeyVisitor.Visit(preTransformResult2.Expression, parameters2, Sfi);
226226

227227
Assert.That(parameters1, Has.Count.GreaterThan(0), "parameters1");
228228
Assert.That(parameters2, Has.Count.GreaterThan(0), "parameters2");

src/NHibernate.Test/Linq/EnumTests.cs

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,5 +48,42 @@ public void CanQueryOnEnumStoredAsString(EnumStoredAsString type, int expectedCo
4848

4949
Assert.AreEqual(expectedCount, query.Count);
5050
}
51+
52+
[Test]
53+
public void ConditionalNavigationProperty()
54+
{
55+
EnumStoredAsString? type = null;
56+
db.Users.Where(o => o.Enum1 == EnumStoredAsString.Large).ToList();
57+
db.Users.Where(o => EnumStoredAsString.Large != o.Enum1).ToList();
58+
db.Users.Where(o => (o.NullableEnum1 ?? EnumStoredAsString.Large) == EnumStoredAsString.Medium).ToList();
59+
db.Users.Where(o => ((o.NullableEnum1 ?? type) ?? o.Enum1) == EnumStoredAsString.Medium).ToList();
60+
61+
db.Users.Where(o => (o.NullableEnum1.HasValue ? o.Enum1 : EnumStoredAsString.Unspecified) == EnumStoredAsString.Medium).ToList();
62+
db.Users.Where(o => (o.Enum1 != EnumStoredAsString.Large
63+
? (o.NullableEnum1.HasValue ? o.Enum1 : EnumStoredAsString.Unspecified)
64+
: EnumStoredAsString.Small) == EnumStoredAsString.Medium).ToList();
65+
66+
db.Users.Where(o => (o.Enum1 == EnumStoredAsString.Large ? o.Role : o.Role).Name == "test").ToList();
67+
}
68+
69+
[Test]
70+
public void CanQueryComplexExpressionOnEnumStoredAsString()
71+
{
72+
var type = EnumStoredAsString.Unspecified;
73+
var query = (from user in db.Users
74+
where (user.NullableEnum1 == EnumStoredAsString.Large
75+
? EnumStoredAsString.Medium
76+
: user.NullableEnum1 ?? user.Enum1
77+
) == type
78+
select new
79+
{
80+
user,
81+
simple = user.Enum1,
82+
condition = user.Enum1 == EnumStoredAsString.Large ? EnumStoredAsString.Medium : user.Enum1,
83+
coalesce = user.NullableEnum1 ?? EnumStoredAsString.Medium
84+
}).ToList();
85+
86+
Assert.That(query.Count, Is.EqualTo(0));
87+
}
5188
}
5289
}

src/NHibernate.Test/Linq/LinqTestCase.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,8 @@ protected override string[] Mappings
3434
"Northwind.Mappings.User.hbm.xml",
3535
"Northwind.Mappings.TimeSheet.hbm.xml",
3636
"Northwind.Mappings.Animal.hbm.xml",
37-
"Northwind.Mappings.Patient.hbm.xml"
37+
"Northwind.Mappings.Patient.hbm.xml",
38+
"Northwind.Mappings.DynamicUser.hbm.xml"
3839
};
3940
}
4041
}

src/NHibernate.Test/Linq/ParameterTests.cs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,34 @@ public void UsingTwoEntityParameters()
7676
2);
7777
}
7878

79+
[Test]
80+
public void UsingEntityEnumerableParameterTwice()
81+
{
82+
if (!Dialect.SupportsSubSelects)
83+
{
84+
Assert.Ignore();
85+
}
86+
87+
var enumerable = db.DynamicUsers.First();
88+
AssertTotalParameters(
89+
db.DynamicUsers.Where(o => o == enumerable && o != enumerable),
90+
1);
91+
}
92+
93+
[Test]
94+
public void UsingEntityEnumerableListParameterTwice()
95+
{
96+
if (!Dialect.SupportsSubSelects)
97+
{
98+
Assert.Ignore();
99+
}
100+
101+
var enumerable = new[] {db.DynamicUsers.First()};
102+
AssertTotalParameters(
103+
db.DynamicUsers.Where(o => enumerable.Contains(o) && enumerable.Contains(o)),
104+
1);
105+
}
106+
79107
[Test]
80108
public void UsingValueTypeParameterTwice()
81109
{

0 commit comments

Comments
 (0)