Skip to content

Commit d8cde71

Browse files
gliljashazzik
andauthored
Handle implicit convert expression in LINQ update with anonymous type (#3717)
Co-authored-by: Alex Zaytsev <hazzik@gmail.com>
1 parent 2b0e1f1 commit d8cde71

File tree

5 files changed

+92
-7
lines changed

5 files changed

+92
-7
lines changed
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
Namespace Issues.GH3716
2+
Public Class Entity
3+
Public Overridable Property Id As Guid
4+
5+
Public Overridable Property Date1 As Date?
6+
End Class
7+
End Namespace
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
Imports NHibernate.Linq
2+
Imports NUnit.Framework
3+
4+
Namespace Issues.GH3716
5+
<TestFixture>
6+
Public Class Fixture
7+
Inherits IssueTestCase
8+
9+
Protected Overrides Sub OnSetUp()
10+
11+
Using session As ISession = OpenSession()
12+
13+
Using transaction As ITransaction = session.BeginTransaction()
14+
15+
Dim e1 = New Entity
16+
e1.Date1 = New Date(2017, 12, 3)
17+
session.Save(e1)
18+
19+
Dim e2 = New Entity
20+
e2.Date1 = New Date(2017, 12, 1)
21+
session.Save(e2)
22+
23+
Dim e3 = New Entity
24+
session.Save(e3)
25+
26+
session.Flush()
27+
transaction.Commit()
28+
29+
End Using
30+
31+
End Using
32+
End Sub
33+
34+
Protected Overrides Sub OnTearDown()
35+
36+
Using session As ISession = OpenSession()
37+
38+
Using transaction As ITransaction = session.BeginTransaction()
39+
40+
session.Delete("from System.Object")
41+
42+
session.Flush()
43+
transaction.Commit()
44+
45+
End Using
46+
47+
End Using
48+
End Sub
49+
50+
<Test>
51+
Public Sub ShouldBeAbleToUpdateWithAnonymousType()
52+
53+
Using session As ISession = OpenSession()
54+
session.Query(Of Entity).Update(Function(x) New With {.Date1 = Date.Today})
55+
End Using
56+
End Sub
57+
End Class
58+
End Namespace
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<?xml version="1.0" encoding="utf-8" ?>
2+
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="NHibernate.Test.VisualBasic" namespace="NHibernate.Test.VisualBasic.Issues.GH3716">
3+
4+
<class name="Entity">
5+
<id name="Id">
6+
<generator class="guid.comb"/>
7+
</id>
8+
<property name="Date1" />
9+
</class>
10+
11+
</hibernate-mapping>

src/NHibernate.Test.VisualBasic/NHibernate.Test.VisualBasic.vbproj

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@
2626
</ItemGroup>
2727
<ItemGroup>
2828
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.0.0" />
29-
<PackageReference Include="Microsoft.VisualBasic" Version="10.3.0" />
3029
<PackageReference Include="NUnit" Version="3.14.0" />
3130
<PackageReference Include="NUnit3TestAdapter" Version="4.6.0" />
3231
</ItemGroup>

src/NHibernate/Linq/DmlExpressionRewriter.cs

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,10 @@ void AddSettersFromBindings(IEnumerable<MemberBinding> bindings, string path)
2525
switch (node.BindingType)
2626
{
2727
case MemberBindingType.Assignment:
28-
AddSettersFromAssignment((MemberAssignment)node, subPath);
28+
AddSettersFromAssignment((MemberAssignment) node, subPath);
2929
break;
3030
case MemberBindingType.MemberBinding:
31-
AddSettersFromBindings(((MemberMemberBinding)node).Bindings, subPath);
31+
AddSettersFromBindings(((MemberMemberBinding) node).Bindings, subPath);
3232
break;
3333
default:
3434
throw new InvalidOperationException($"{node.BindingType} is not supported");
@@ -53,10 +53,10 @@ void AddSettersFromAnonymousConstructor(NewExpression newExpression, string path
5353
switch (argument.NodeType)
5454
{
5555
case ExpressionType.New:
56-
AddSettersFromAnonymousConstructor((NewExpression)argument, subPath);
56+
AddSettersFromAnonymousConstructor((NewExpression) argument, subPath);
5757
break;
5858
case ExpressionType.MemberInit:
59-
AddSettersFromBindings(((MemberInitExpression)argument).Bindings, subPath);
59+
AddSettersFromBindings(((MemberInitExpression) argument).Bindings, subPath);
6060
break;
6161
default:
6262
_assignments.Add(subPath.Substring(1), Expression.Lambda(argument, _parameters));
@@ -121,15 +121,25 @@ public static Expression PrepareExpressionFromAnonymous<TSource>(Expression sour
121121
if (expression == null)
122122
throw new ArgumentNullException(nameof(expression));
123123

124-
// Anonymous initializations are not implemented as member initialization but as plain constructor call.
125-
var newExpression = expression.Body as NewExpression ??
124+
// Anonymous initializations are not implemented as member initialization but as plain constructor call, potentially wrapped in a Convert expression
125+
var newExpression = UnwrapConvertExpression(expression.Body) as NewExpression ??
126126
throw new ArgumentException("The expression must be an anonymous initialization, e.g. x => new { Name = x.Name, Age = x.Age + 5 }");
127127

128128
var instance = new DmlExpressionRewriter(expression.Parameters);
129129
instance.AddSettersFromAnonymousConstructor(newExpression, "");
130130
return PrepareExpression<TSource>(sourceExpression, instance._assignments);
131131
}
132132

133+
private static Expression UnwrapConvertExpression(Expression expression)
134+
{
135+
if (expression is UnaryExpression ue && ue.NodeType == ExpressionType.Convert)
136+
{
137+
return ue.Operand;
138+
}
139+
140+
return expression;
141+
}
142+
133143
public static Expression PrepareExpression<TSource>(Expression sourceExpression, IReadOnlyDictionary<string, Expression> assignments)
134144
{
135145
var lambda = ConvertAssignmentsToBlockExpression<TSource>(assignments);

0 commit comments

Comments
 (0)