Skip to content

Commit 426fe50

Browse files
committed
Add support for querying dynamic objects with Linq
1 parent 9f2997c commit 426fe50

File tree

8 files changed

+329
-2
lines changed

8 files changed

+329
-2
lines changed

src/NHibernate.Test/Async/Extralazy/ExtraLazyFixture.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
//------------------------------------------------------------------------------
1+
//------------------------------------------------------------------------------
22
// <auto-generated>
33
// This code was generated by AsyncGenerator.
44
//
Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
//------------------------------------------------------------------------------
2+
// <auto-generated>
3+
// This code was generated by AsyncGenerator.
4+
//
5+
// Changes to this file may cause incorrect behavior and will be lost if
6+
// the code is regenerated.
7+
// </auto-generated>
8+
//------------------------------------------------------------------------------
9+
10+
11+
using System.Collections;
12+
using System.Linq;
13+
using NUnit.Framework;
14+
using System.Collections.Generic;
15+
using System.Linq.Dynamic.Core;
16+
using NHibernate.Linq;
17+
18+
namespace NHibernate.Test.NHSpecificTest.NH2664Dynamic
19+
{
20+
using System.Threading.Tasks;
21+
[TestFixture]
22+
public class FixtureAsync : TestCase
23+
{
24+
protected override string MappingsAssembly => "NHibernate.Test";
25+
26+
protected override IList Mappings => new[] {"NHSpecificTest.NH2664Dynamic.Mappings.hbm.xml"};
27+
28+
/// <summary>
29+
/// push some data into the database
30+
/// Really functions as a save test also
31+
/// </summary>
32+
protected override void OnSetUp()
33+
{
34+
using (var session = OpenSession())
35+
using (var tran = session.BeginTransaction())
36+
{
37+
session.Save(
38+
new Product
39+
{
40+
ProductId = "1",
41+
Properties = new Dictionary<string, object>
42+
{
43+
["Name"] = "First Product",
44+
["Description"] = "First Description"
45+
}
46+
});
47+
48+
session.Save(
49+
new Product
50+
{
51+
ProductId = "2",
52+
Properties = new Dictionary<string, object>
53+
{
54+
["Name"] = "Second Product",
55+
["Description"] = "Second Description"
56+
}
57+
});
58+
59+
session.Save(
60+
new Product
61+
{
62+
ProductId = "3",
63+
Properties = new Dictionary<string, object>
64+
{
65+
["Name"] = "val",
66+
["Description"] = "val"
67+
}
68+
});
69+
70+
tran.Commit();
71+
}
72+
}
73+
74+
protected override void OnTearDown()
75+
{
76+
using (var session = OpenSession())
77+
using (var tran = session.BeginTransaction())
78+
{
79+
session.CreateQuery("delete from Product").ExecuteUpdate();
80+
tran.Commit();
81+
}
82+
}
83+
84+
[Test]
85+
public async Task Query_DynamicComponentAsync()
86+
{
87+
using (var session = OpenSession())
88+
{
89+
var product = await (session
90+
.Query<Product>()
91+
.Where("Properties.Name == @0", "First Product")
92+
.SingleAsync());
93+
94+
Assert.That(product, Is.Not.Null);
95+
Assert.That((object) product.Properties["Name"], Is.EqualTo("First Product"));
96+
}
97+
}
98+
99+
[Test]
100+
public async Task Multiple_Query_Does_Not_CacheAsync()
101+
{
102+
using (var session = OpenSession())
103+
{
104+
// Query by name
105+
var product1 = await (session.Query<Product>().Where("Properties.Name == @0", "First Product").SingleAsync());
106+
Assert.That(product1.ProductId, Is.EqualTo("1"));
107+
108+
// Query by description (this test is to verify that the dictionary
109+
// index isn't cached from the query above.
110+
var product2 = await (session.Query<Product>().Where("Properties.Description == @0", "Second Description").SingleAsync());
111+
Assert.That(product2.ProductId, Is.EqualTo("2"));
112+
}
113+
}
114+
}
115+
}
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
using System.Collections;
2+
using System.Linq;
3+
using NUnit.Framework;
4+
using System.Collections.Generic;
5+
using System.Linq.Dynamic.Core;
6+
7+
namespace NHibernate.Test.NHSpecificTest.NH2664Dynamic
8+
{
9+
[TestFixture]
10+
public class Fixture : TestCase
11+
{
12+
protected override string MappingsAssembly => "NHibernate.Test";
13+
14+
protected override IList Mappings => new[] {"NHSpecificTest.NH2664Dynamic.Mappings.hbm.xml"};
15+
16+
/// <summary>
17+
/// push some data into the database
18+
/// Really functions as a save test also
19+
/// </summary>
20+
protected override void OnSetUp()
21+
{
22+
using (var session = OpenSession())
23+
using (var tran = session.BeginTransaction())
24+
{
25+
session.Save(
26+
new Product
27+
{
28+
ProductId = "1",
29+
Properties = new Dictionary<string, object>
30+
{
31+
["Name"] = "First Product",
32+
["Description"] = "First Description"
33+
}
34+
});
35+
36+
session.Save(
37+
new Product
38+
{
39+
ProductId = "2",
40+
Properties = new Dictionary<string, object>
41+
{
42+
["Name"] = "Second Product",
43+
["Description"] = "Second Description"
44+
}
45+
});
46+
47+
session.Save(
48+
new Product
49+
{
50+
ProductId = "3",
51+
Properties = new Dictionary<string, object>
52+
{
53+
["Name"] = "val",
54+
["Description"] = "val"
55+
}
56+
});
57+
58+
tran.Commit();
59+
}
60+
}
61+
62+
protected override void OnTearDown()
63+
{
64+
using (var session = OpenSession())
65+
using (var tran = session.BeginTransaction())
66+
{
67+
session.CreateQuery("delete from Product").ExecuteUpdate();
68+
tran.Commit();
69+
}
70+
}
71+
72+
[Test]
73+
public void Query_DynamicComponent()
74+
{
75+
using (var session = OpenSession())
76+
{
77+
var product = session
78+
.Query<Product>()
79+
.Where("Properties.Name == @0", "First Product")
80+
.Single();
81+
82+
Assert.That(product, Is.Not.Null);
83+
Assert.That((object) product.Properties["Name"], Is.EqualTo("First Product"));
84+
}
85+
}
86+
87+
[Test]
88+
public void Multiple_Query_Does_Not_Cache()
89+
{
90+
using (var session = OpenSession())
91+
{
92+
// Query by name
93+
var product1 = session.Query<Product>().Where("Properties.Name == @0", "First Product").Single();
94+
Assert.That(product1.ProductId, Is.EqualTo("1"));
95+
96+
// Query by description (this test is to verify that the dictionary
97+
// index isn't cached from the query above.
98+
var product2 = session.Query<Product>().Where("Properties.Description == @0", "Second Description").Single();
99+
Assert.That(product2.ProductId, Is.EqualTo("2"));
100+
}
101+
}
102+
}
103+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<?xml version="1.0" encoding="utf-8" ?>
2+
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="NHibernate.Test"
3+
namespace="NHibernate.Test.NHSpecificTest.NH2664Dynamic">
4+
<class name="Product" table="Products">
5+
<id name="ProductId" column="ProductId" type="String">
6+
<generator class="assigned" />
7+
8+
</id>
9+
<dynamic-component name="Properties">
10+
<property name="Description" column="Description" type="String"/>
11+
<property name="Name" column="Name" type="String" />
12+
13+
</dynamic-component>
14+
15+
</class>
16+
17+
</hibernate-mapping>
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
namespace NHibernate.Test.NHSpecificTest.NH2664Dynamic
2+
{
3+
public class Product
4+
{
5+
public virtual string ProductId { get; set; }
6+
7+
public virtual dynamic Properties { get; set; }
8+
}
9+
}

src/NHibernate.Test/NHibernate.Test.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@
4848
</ItemGroup>
4949
<ItemGroup>
5050
<PackageReference Include="log4net" Version="2.0.8" />
51-
<PackageReference Include="System.Linq.Dynamic.Core" Version="1.0.8" />
51+
<PackageReference Include="System.Linq.Dynamic.Core" Version="1.0.8.11" />
5252
<PackageReference Include="NSubstitute" Version="3.1.0" />
5353
<PackageReference Include="NUnit" Version="3.10.1" />
5454
<PackageReference Include="NUnit3TestAdapter" Version="3.10.0" />

src/NHibernate/Linq/Visitors/ExpressionKeyVisitor.cs

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
using System;
22
using System.Collections;
33
using System.Collections.Generic;
4+
using System.Dynamic;
45
using System.Linq;
56
using System.Linq.Expressions;
67
using System.Reflection;
8+
using System.Runtime.CompilerServices;
79
using System.Text;
810
using NHibernate.Param;
911
using Remotion.Linq.Parsing;
@@ -214,6 +216,13 @@ protected override Expression VisitQuerySourceReference(Remotion.Linq.Clauses.Ex
214216
return expression;
215217
}
216218

219+
protected override Expression VisitDynamic(DynamicExpression expression)
220+
{
221+
FormatBinder(expression.Binder);
222+
Visit(expression.Arguments, AppendCommas);
223+
return expression;
224+
}
225+
217226
private void VisitMethod(MethodInfo methodInfo)
218227
{
219228
_string.Append(methodInfo.Name);
@@ -224,5 +233,51 @@ private void VisitMethod(MethodInfo methodInfo)
224233
_string.Append(']');
225234
}
226235
}
236+
237+
private void FormatBinder(CallSiteBinder binder)
238+
{
239+
switch (binder)
240+
{
241+
case ConvertBinder b:
242+
_string.Append("Convert ").Append(b.Type);
243+
break;
244+
case CreateInstanceBinder _:
245+
_string.Append("Create");
246+
break;
247+
case DeleteIndexBinder _:
248+
_string.Append("DeleteIndex");
249+
break;
250+
case DeleteMemberBinder b:
251+
_string.Append("DeleteMember ").Append(b.Name);
252+
break;
253+
case BinaryOperationBinder b:
254+
_string.Append(b.Operation);
255+
break;
256+
case GetIndexBinder _:
257+
_string.Append("GetIndex");
258+
break;
259+
case GetMemberBinder b:
260+
_string.Append("GetMember ").Append(b.Name);
261+
break;
262+
case InvokeBinder _:
263+
_string.Append("Invoke");
264+
break;
265+
case InvokeMemberBinder b:
266+
_string.Append("InvokeMember ").Append(b.Name);
267+
break;
268+
case SetIndexBinder _:
269+
_string.Append("SetIndex");
270+
break;
271+
case SetMemberBinder b:
272+
_string.Append("SetMember ").Append(b.Name);
273+
break;
274+
case UnaryOperationBinder b:
275+
_string.Append(b.Operation);
276+
break;
277+
case DynamicMetaObjectBinder _:
278+
_string.Append("DynamicMetaObject");
279+
break;
280+
}
281+
}
227282
}
228283
}

0 commit comments

Comments
 (0)