Skip to content

Commit 42e537b

Browse files
Implements support for NewArrayInit LINQ expressions (NH-2678)
1 parent d8e26fa commit 42e537b

File tree

4 files changed

+138
-19
lines changed

4 files changed

+138
-19
lines changed

src/NHibernate.Test/Linq/MethodCallTests.cs

Lines changed: 102 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
using System.Linq;
1+
using System;
2+
using System.Linq;
3+
using NHibernate.DomainModel.Northwind.Entities;
24
using NUnit.Framework;
35

46
namespace NHibernate.Test.Linq
@@ -14,7 +16,7 @@ public void CanExecuteAny()
1416
}
1517

1618
[Test]
17-
public void CanExecuteAnyWithArguments()
19+
public void CanExecuteAnyWithArguments()
1820
{
1921
var result = db.Users.Any(u => u.Name == "user-does-not-exist");
2022
Assert.IsFalse(result);
@@ -27,5 +29,102 @@ public void CanExecuteCountWithOrderByArguments()
2729
var count = query.Count();
2830
Assert.AreEqual(3, count);
2931
}
32+
33+
[Test, Description("NH-2744")]
34+
public void CanSelectPropertiesIntoObjectArray()
35+
{
36+
var result = db.Users
37+
.Select(u => new object[] {u.Id, u.Name, u.InvalidLoginAttempts})
38+
.First();
39+
40+
Assert.That(result.Length, Is.EqualTo(3));
41+
Assert.That(result[0], Is.EqualTo(1));
42+
Assert.That(result[1], Is.EqualTo("ayende"));
43+
Assert.That(result[2], Is.EqualTo(4));
44+
}
45+
46+
[Test, Description("NH-2744")]
47+
public void CanSelectComponentsIntoObjectArray()
48+
{
49+
var result = db.Users
50+
.Select(u => new object[] {u.Component, u.Component.OtherComponent})
51+
.First();
52+
53+
Assert.That(result.Length, Is.EqualTo(2));
54+
Assert.That(result[0], Is.TypeOf<UserComponent>());
55+
Assert.That(result[1], Is.TypeOf<UserComponent2>());
56+
57+
var component = (UserComponent)result[0];
58+
Assert.That(component.Property1, Is.EqualTo("test1"));
59+
60+
var otherComponent = (UserComponent2) result[1];
61+
Assert.That(otherComponent.OtherProperty1, Is.EqualTo("othertest1"));
62+
}
63+
64+
[Test, Description("NH-2744")]
65+
public void CanSelectEnumerationPropertiesIntoObjectArray()
66+
{
67+
var result = db.Users
68+
.Where(u => u.Name == "nhibernate")
69+
.Select(u => new object[] {u.Enum1, u.Enum2, u.Features})
70+
.First();
71+
72+
Assert.That(result.Length, Is.EqualTo(3));
73+
Assert.That(result[0], Is.EqualTo(EnumStoredAsString.Medium));
74+
Assert.That(result[1], Is.EqualTo(EnumStoredAsInt32.Unspecified));
75+
Assert.That(result[2], Is.EqualTo(FeatureSet.HasAll));
76+
}
77+
78+
[Test, Description("NH-2744")]
79+
public void CanSelectConstantsIntoObjectArray()
80+
{
81+
const decimal pi = 3.1415m;
82+
const string name = "Julian";
83+
84+
var result = db.Users
85+
.Select(u => new object[] {u.Id, pi, name, DateTime.MinValue})
86+
.First();
87+
88+
Assert.That(result.Length, Is.EqualTo(4));
89+
Assert.That(result[0], Is.EqualTo(1));
90+
Assert.That(result[1], Is.EqualTo(pi));
91+
Assert.That(result[2], Is.EqualTo(name));
92+
Assert.That(result[3], Is.EqualTo(DateTime.MinValue));
93+
}
94+
95+
[Test, Description("NH-2744")]
96+
public void CanSelectPropertiesFromAssociationsIntoObjectArray()
97+
{
98+
var result = db.Users
99+
.Select(u => new object[] {u.Id, u.Role.Name, u.Role.Entity.Output})
100+
.First();
101+
102+
Assert.That(result.Length, Is.EqualTo(3));
103+
Assert.That(result[0], Is.EqualTo(1));
104+
Assert.That(result[1], Is.EqualTo("Admin"));
105+
Assert.That(result[2], Is.EqualTo("output"));
106+
}
107+
108+
[Test, Description("NH-2744")]
109+
public void CanSelectPropertiesIntoNestedObjectArrays()
110+
{
111+
var query = db.Users.Select(u => new object[] {"Root", new object[] {"Sub1", u.Name, new object[] {"Sub2", u.Name}}});
112+
var result = query.First();
113+
114+
Assert.That(result.Length, Is.EqualTo(2));
115+
Assert.That(result[0], Is.EqualTo("Root"));
116+
Assert.That(result[1], Is.TypeOf<object[]>());
117+
118+
var nestedObjectArray = (object[]) result[1];
119+
Assert.That(nestedObjectArray.Length, Is.EqualTo(3));
120+
Assert.That(nestedObjectArray[0], Is.EqualTo("Sub1"));
121+
Assert.That(nestedObjectArray[1], Is.EqualTo("ayende"));
122+
Assert.That(nestedObjectArray[2], Is.TypeOf<object[]>());
123+
124+
var nestedNestedObjectArray = (object[]) nestedObjectArray[2];
125+
Assert.That(nestedNestedObjectArray.Length, Is.EqualTo(2));
126+
Assert.That(nestedNestedObjectArray[0], Is.EqualTo("Sub2"));
127+
Assert.That(nestedNestedObjectArray[1], Is.EqualTo("ayende"));
128+
}
30129
}
31-
}
130+
}

src/NHibernate/Hql/Ast/HqlTreeBuilder.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -371,6 +371,16 @@ public HqlDistinctHolder DistinctHolder(params HqlTreeNode[] children)
371371
return new HqlDistinctHolder(_factory, children);
372372
}
373373

374+
public HqlExpressionSubTreeHolder ExpressionSubTreeHolder(params HqlTreeNode[] children)
375+
{
376+
return new HqlExpressionSubTreeHolder(_factory, children);
377+
}
378+
379+
public HqlExpressionSubTreeHolder ExpressionSubTreeHolder(IEnumerable<HqlTreeNode> children)
380+
{
381+
return new HqlExpressionSubTreeHolder(_factory, children);
382+
}
383+
374384
public HqlIsNull IsNull(HqlExpression lhs)
375385
{
376386
return new HqlIsNull(_factory, lhs);

src/NHibernate/Hql/Ast/HqlTreeNode.cs

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -31,15 +31,7 @@ private void AddChildren(IEnumerable<HqlTreeNode> children)
3131
{
3232
if (child != null)
3333
{
34-
if (child is HqlDistinctHolder)
35-
{
36-
AddChildren(child.Children);
37-
}
38-
else
39-
{
40-
_children.Add(child);
41-
_node.AddChild(child.AstNode);
42-
}
34+
AddChild(child);
4335
}
4436
}
4537
}
@@ -99,7 +91,7 @@ internal IASTNode AstNode
9991

10092
internal void AddChild(HqlTreeNode child)
10193
{
102-
if (child is HqlDistinctHolder)
94+
if ((child is HqlExpressionSubTreeHolder) || (child is HqlDistinctHolder))
10395
{
10496
AddChildren(child.Children);
10597
}
@@ -129,7 +121,6 @@ public static HqlBooleanExpression AsBooleanExpression(this HqlTreeNode node)
129121
// TODO - nice error handling if cast fails
130122
return (HqlBooleanExpression)node;
131123
}
132-
133124
}
134125

135126
public abstract class HqlStatement : HqlTreeNode
@@ -179,7 +170,6 @@ internal HqlQuery(IASTFactory factory, params HqlStatement[] children)
179170
}
180171
}
181172

182-
183173
public class HqlIdent : HqlExpression
184174
{
185175
internal HqlIdent(IASTFactory factory, string ident)
@@ -849,13 +839,25 @@ public HqlBooleanMethodCall(IASTFactory factory, string methodName, IEnumerable<
849839
}
850840
}
851841

842+
[Obsolete("Use HqlExpressionSubTreeHolder instead")]
852843
public class HqlDistinctHolder : HqlExpression
853844
{
854845
public HqlDistinctHolder(IASTFactory factory, HqlTreeNode[] children) : base(int.MinValue, "distinct holder", factory, children)
855846
{
856847
}
857848
}
858849

850+
public class HqlExpressionSubTreeHolder : HqlExpression
851+
{
852+
public HqlExpressionSubTreeHolder(IASTFactory factory, HqlTreeNode[] children) : base(int.MinValue, "expression sub-tree holder", factory, children)
853+
{
854+
}
855+
856+
public HqlExpressionSubTreeHolder(IASTFactory factory, IEnumerable<HqlTreeNode> children) : base(int.MinValue, "expression sub-tree holder", factory, children)
857+
{
858+
}
859+
}
860+
859861
public class HqlIsNull : HqlBooleanExpression
860862
{
861863
public HqlIsNull(IASTFactory factory, HqlExpression lhs)

src/NHibernate/Linq/Visitors/HqlGeneratorExpressionTreeVisitor.cs

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System;
2+
using System.Linq;
23
using System.Linq.Expressions;
34
using NHibernate.Engine.Query;
45
using NHibernate.Hql.Ast;
@@ -90,9 +91,9 @@ protected HqlTreeNode VisitExpression(Expression expression)
9091
return VisitMethodCallExpression((MethodCallExpression) expression);
9192
//case ExpressionType.New:
9293
// return VisitNewExpression((NewExpression)expression);
93-
case ExpressionType.NewArrayBounds:
94-
//case ExpressionType.NewArrayInit:
95-
// return VisitNewArrayExpression((NewArrayExpression)expression);
94+
//case ExpressionType.NewArrayBounds:
95+
case ExpressionType.NewArrayInit:
96+
return VisitNewArrayExpression((NewArrayExpression) expression);
9697
//case ExpressionType.MemberInit:
9798
// return VisitMemberInitExpression((MemberInitExpression)expression);
9899
//case ExpressionType.ListInit:
@@ -181,7 +182,7 @@ protected HqlTreeNode VisitNhSum(NhSumExpression expression)
181182
protected HqlTreeNode VisitNhDistinct(NhDistinctExpression expression)
182183
{
183184
var visitor = new HqlGeneratorExpressionTreeVisitor(_parameters);
184-
return _hqlTreeBuilder.DistinctHolder(_hqlTreeBuilder.Distinct(), visitor.VisitExpression(expression.Expression));
185+
return _hqlTreeBuilder.ExpressionSubTreeHolder(_hqlTreeBuilder.Distinct(), visitor.VisitExpression(expression.Expression));
185186
}
186187

187188
protected HqlTreeNode VisitQuerySourceReferenceExpression(QuerySourceReferenceExpression expression)
@@ -452,5 +453,12 @@ protected HqlTreeNode VisitSubQueryExpression(SubQueryExpression expression)
452453
ExpressionToHqlTranslationResults query = QueryModelVisitor.GenerateHqlQuery(expression.QueryModel, _parameters, false);
453454
return query.Statement;
454455
}
456+
457+
protected HqlTreeNode VisitNewArrayExpression(NewArrayExpression expression)
458+
{
459+
var visitor = new HqlGeneratorExpressionTreeVisitor(_parameters);
460+
var expressionSubTree = expression.Expressions.Select(visitor.Visit);
461+
return _hqlTreeBuilder.ExpressionSubTreeHolder(expressionSubTree);
462+
}
455463
}
456464
}

0 commit comments

Comments
 (0)