Skip to content

Add Linq parameter type detection #2365

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 11 commits into from
Jul 14, 2020
18 changes: 18 additions & 0 deletions src/NHibernate.DomainModel/Northwind/Entities/DynamicUser.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
using System.Collections;

namespace NHibernate.DomainModel.Northwind.Entities
{
public class DynamicUser : IEnumerable
{
public virtual int Id { get; set; }

public virtual dynamic Properties { get; set; }

public virtual IDictionary Settings { get; set; }

public virtual IEnumerator GetEnumerator()
{
throw new System.NotImplementedException();
}
}
}
5 changes: 5 additions & 0 deletions src/NHibernate.DomainModel/Northwind/Entities/Northwind.cs
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,11 @@ public IQueryable<User> Users
get { return _session.Query<User>(); }
}

public IQueryable<DynamicUser> DynamicUsers
{
get { return _session.Query<DynamicUser>(); }
}

public IQueryable<PatientRecord> PatientRecords
{
get { return _session.Query<PatientRecord>(); }
Expand Down
6 changes: 6 additions & 0 deletions src/NHibernate.DomainModel/Northwind/Entities/User.cs
Original file line number Diff line number Diff line change
Expand Up @@ -48,10 +48,16 @@ public class User : IUser, IEntity

public virtual FeatureSet Features { get; set; }

public virtual User NotMappedUser => this;

public virtual EnumStoredAsString Enum1 { get; set; }

public virtual EnumStoredAsString? NullableEnum1 { get; set; }

public virtual EnumStoredAsInt32 Enum2 { get; set; }

public virtual EnumStoredAsInt32? NullableEnum2 { get; set; }

public virtual IUser CreatedBy { get; set; }

public virtual IUser ModifiedBy { get; set; }
Expand Down
30 changes: 30 additions & 0 deletions src/NHibernate.DomainModel/Northwind/Mappings/DynamicUser.hbm.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" namespace="NHibernate.DomainModel.Northwind.Entities" assembly="NHibernate.DomainModel">
<class name="DynamicUser" mutable="false">
<subselect>
select * from Users
</subselect>

<id name="Id" column="UserId" type="Int32">
<generator class="assigned" />
</id>

<dynamic-component name="Properties">
<property name="Name" type="AnsiString" />
<property name="Enum1" type="NHibernate.DomainModel.Northwind.Entities.EnumStoredAsStringType, NHibernate.DomainModel">
<column name="Enum1" length="12" />
</property>
<many-to-one name="CreatedBy" class="User" not-null="true" lazy="false">
<column name="CreatedById" not-null="true" />
</many-to-one>
</dynamic-component>

<dynamic-component name="Settings">
<property name="Property1" type="AnsiString" />
<property name="Property2" type="AnsiString" />
<many-to-one name="ModifiedBy" class="User" lazy="false">
<column name="ModifiedById" />
</many-to-one>
</dynamic-component>
</class>
</hibernate-mapping>
6 changes: 6 additions & 0 deletions src/NHibernate.DomainModel/Northwind/Mappings/User.hbm.xml
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,14 @@
<column name="Enum1" length="12" />
</property>

<property name="NullableEnum1" type="NHibernate.DomainModel.Northwind.Entities.EnumStoredAsStringType, NHibernate.DomainModel"
formula="(case when Enum1 = 'Unspecified' then null else Enum1 end)" insert="false" update="false">
</property>

<property name="Enum2" not-null="true" />

<property name="NullableEnum2" formula="(case when Enum2 = 0 then null else Enum2 end)" insert="false" update="false" />

<property name="Features" not-null="true" />

<many-to-one name="Role" class="Role">
Expand Down
37 changes: 37 additions & 0 deletions src/NHibernate.Test/Async/Linq/EnumTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -61,5 +61,42 @@ public async Task CanQueryOnEnumStoredAsString_Small_1Async()

Assert.AreEqual(expectedCount, query.Count);
}

[Test]
public async Task ConditionalNavigationPropertyAsync()
{
EnumStoredAsString? type = null;
await (db.Users.Where(o => o.Enum1 == EnumStoredAsString.Large).ToListAsync());
await (db.Users.Where(o => EnumStoredAsString.Large != o.Enum1).ToListAsync());
await (db.Users.Where(o => (o.NullableEnum1 ?? EnumStoredAsString.Large) == EnumStoredAsString.Medium).ToListAsync());
await (db.Users.Where(o => ((o.NullableEnum1 ?? type) ?? o.Enum1) == EnumStoredAsString.Medium).ToListAsync());

await (db.Users.Where(o => (o.NullableEnum1.HasValue ? o.Enum1 : EnumStoredAsString.Unspecified) == EnumStoredAsString.Medium).ToListAsync());
await (db.Users.Where(o => (o.Enum1 != EnumStoredAsString.Large
? (o.NullableEnum1.HasValue ? o.Enum1 : EnumStoredAsString.Unspecified)
: EnumStoredAsString.Small) == EnumStoredAsString.Medium).ToListAsync());

await (db.Users.Where(o => (o.Enum1 == EnumStoredAsString.Large ? o.Role : o.Role).Name == "test").ToListAsync());
}

[Test]
public async Task CanQueryComplexExpressionOnEnumStoredAsStringAsync()
{
var type = EnumStoredAsString.Unspecified;
var query = await ((from user in db.Users
where (user.NullableEnum1 == EnumStoredAsString.Large
? EnumStoredAsString.Medium
: user.NullableEnum1 ?? user.Enum1
) == type
select new
{
user,
simple = user.Enum1,
condition = user.Enum1 == EnumStoredAsString.Large ? EnumStoredAsString.Medium : user.Enum1,
coalesce = user.NullableEnum1 ?? EnumStoredAsString.Medium
}).ToListAsync());

Assert.That(query.Count, Is.EqualTo(0));
}
}
}
28 changes: 28 additions & 0 deletions src/NHibernate.Test/Async/Linq/ParameterTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,34 @@ public async Task UsingTwoEntityParametersAsync()
2));
}

[Test]
public async Task UsingEntityEnumerableParameterTwiceAsync()
{
if (!Dialect.SupportsSubSelects)
{
Assert.Ignore();
}

var enumerable = await (db.DynamicUsers.FirstAsync());
await (AssertTotalParametersAsync(
db.DynamicUsers.Where(o => o == enumerable && o != enumerable),
1));
}

[Test]
public async Task UsingEntityEnumerableListParameterTwiceAsync()
{
if (!Dialect.SupportsSubSelects)
{
Assert.Ignore();
}

var enumerable = new[] {await (db.DynamicUsers.FirstAsync())};
await (AssertTotalParametersAsync(
db.DynamicUsers.Where(o => enumerable.Contains(o) && enumerable.Contains(o)),
1));
}

[Test]
public async Task UsingValueTypeParameterTwiceAsync()
{
Expand Down
8 changes: 4 additions & 4 deletions src/NHibernate.Test/Linq/ConstantTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -217,12 +217,12 @@ public void ConstantInWhereDoesNotCauseManyKeys()
select c);
var preTransformParameters = new PreTransformationParameters(QueryMode.Select, Sfi);
var preTransformResult = NhRelinqQueryParser.PreTransform(q1.Expression, preTransformParameters);
var expression = ExpressionParameterVisitor.Visit(preTransformResult, out var parameters1);
var k1 = ExpressionKeyVisitor.Visit(expression, parameters1);
var parameters1 = ExpressionParameterVisitor.Visit(preTransformResult);
var k1 = ExpressionKeyVisitor.Visit(preTransformResult.Expression, parameters1, Sfi);

var preTransformResult2 = NhRelinqQueryParser.PreTransform(q2.Expression, preTransformParameters);
var expression2 = ExpressionParameterVisitor.Visit(preTransformResult2, out var parameters2);
var k2 = ExpressionKeyVisitor.Visit(expression2, parameters2);
var parameters2 = ExpressionParameterVisitor.Visit(preTransformResult2);
var k2 = ExpressionKeyVisitor.Visit(preTransformResult2.Expression, parameters2, Sfi);

Assert.That(parameters1, Has.Count.GreaterThan(0), "parameters1");
Assert.That(parameters2, Has.Count.GreaterThan(0), "parameters2");
Expand Down
37 changes: 37 additions & 0 deletions src/NHibernate.Test/Linq/EnumTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -48,5 +48,42 @@ public void CanQueryOnEnumStoredAsString(EnumStoredAsString type, int expectedCo

Assert.AreEqual(expectedCount, query.Count);
}

[Test]
public void ConditionalNavigationProperty()
{
EnumStoredAsString? type = null;
db.Users.Where(o => o.Enum1 == EnumStoredAsString.Large).ToList();
db.Users.Where(o => EnumStoredAsString.Large != o.Enum1).ToList();
db.Users.Where(o => (o.NullableEnum1 ?? EnumStoredAsString.Large) == EnumStoredAsString.Medium).ToList();
db.Users.Where(o => ((o.NullableEnum1 ?? type) ?? o.Enum1) == EnumStoredAsString.Medium).ToList();

db.Users.Where(o => (o.NullableEnum1.HasValue ? o.Enum1 : EnumStoredAsString.Unspecified) == EnumStoredAsString.Medium).ToList();
db.Users.Where(o => (o.Enum1 != EnumStoredAsString.Large
? (o.NullableEnum1.HasValue ? o.Enum1 : EnumStoredAsString.Unspecified)
: EnumStoredAsString.Small) == EnumStoredAsString.Medium).ToList();

db.Users.Where(o => (o.Enum1 == EnumStoredAsString.Large ? o.Role : o.Role).Name == "test").ToList();
}

[Test]
public void CanQueryComplexExpressionOnEnumStoredAsString()
{
var type = EnumStoredAsString.Unspecified;
var query = (from user in db.Users
where (user.NullableEnum1 == EnumStoredAsString.Large
? EnumStoredAsString.Medium
: user.NullableEnum1 ?? user.Enum1
) == type
select new
{
user,
simple = user.Enum1,
condition = user.Enum1 == EnumStoredAsString.Large ? EnumStoredAsString.Medium : user.Enum1,
coalesce = user.NullableEnum1 ?? EnumStoredAsString.Medium
}).ToList();

Assert.That(query.Count, Is.EqualTo(0));
}
}
}
3 changes: 2 additions & 1 deletion src/NHibernate.Test/Linq/LinqTestCase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,8 @@ protected override string[] Mappings
"Northwind.Mappings.User.hbm.xml",
"Northwind.Mappings.TimeSheet.hbm.xml",
"Northwind.Mappings.Animal.hbm.xml",
"Northwind.Mappings.Patient.hbm.xml"
"Northwind.Mappings.Patient.hbm.xml",
"Northwind.Mappings.DynamicUser.hbm.xml"
};
}
}
Expand Down
28 changes: 28 additions & 0 deletions src/NHibernate.Test/Linq/ParameterTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,34 @@ public void UsingTwoEntityParameters()
2);
}

[Test]
public void UsingEntityEnumerableParameterTwice()
{
if (!Dialect.SupportsSubSelects)
{
Assert.Ignore();
}

var enumerable = db.DynamicUsers.First();
AssertTotalParameters(
db.DynamicUsers.Where(o => o == enumerable && o != enumerable),
1);
}

[Test]
public void UsingEntityEnumerableListParameterTwice()
{
if (!Dialect.SupportsSubSelects)
{
Assert.Ignore();
}

var enumerable = new[] {db.DynamicUsers.First()};
AssertTotalParameters(
db.DynamicUsers.Where(o => enumerable.Contains(o) && enumerable.Contains(o)),
1);
}

[Test]
public void UsingValueTypeParameterTwice()
{
Expand Down
Loading