11using System ;
22using System . Collections . Generic ;
3- using System . Collections . ObjectModel ;
43using System . Linq ;
54using System . Linq . Expressions ;
6- using System . Reflection ;
75using NHibernate . Linq . Visitors ;
8- using NHibernate . Util ;
96
107namespace NHibernate . Linq
118{
12- public abstract class Assignments
13- {
14- protected static readonly ConstructorInfo DictionaryConstructorInfo = typeof ( Dictionary < string , object > ) . GetConstructor ( new [ ] { typeof ( int ) } ) ;
15- protected static readonly MethodInfo DictionaryAddMethodInfo = ReflectHelper . GetMethod < Dictionary < string , object > > ( d => d . Add ( null , null ) ) ;
16- }
17-
189 /// <summary>
1910 /// Class to hold assignments used in updates and inserts.
2011 /// </summary>
2112 /// <typeparam name="TSource">The type of the entity source of the insert or to update.</typeparam>
2213 /// <typeparam name="TTarget">The type of the entity to insert or to update.</typeparam>
23- public class Assignments < TSource , TTarget > : Assignments
14+ public class Assignments < TSource , TTarget >
2415 {
25- private readonly List < Assignment > _sets = new List < Assignment > ( ) ;
16+ private readonly Dictionary < string , Expression > _assignments = new Dictionary < string , Expression > ( ) ;
17+
18+ internal IReadOnlyDictionary < string , Expression > List => _assignments ;
2619
2720 /// <summary>
2821 /// Sets the specified property.
@@ -36,7 +29,7 @@ public Assignments<TSource, TTarget> Set<TProp>(Expression<Func<TTarget, TProp>>
3629 if ( expression == null )
3730 throw new ArgumentNullException ( nameof ( expression ) ) ;
3831 var member = GetMemberExpression ( property ) ;
39- _sets . Add ( new Assignment ( member . GetMemberPath ( ) , expression ) ) ;
32+ _assignments . Add ( member . GetMemberPath ( ) , expression ) ;
4033 return this ;
4134 }
4235
@@ -50,7 +43,7 @@ public Assignments<TSource, TTarget> Set<TProp>(Expression<Func<TTarget, TProp>>
5043 public Assignments < TSource , TTarget > Set < TProp > ( Expression < Func < TTarget , TProp > > property , TProp value )
5144 {
5245 var member = GetMemberExpression ( property ) ;
53- _sets . Add ( new Assignment ( member . GetMemberPath ( ) , Expression . Constant ( value , typeof ( TProp ) ) ) ) ;
46+ _assignments . Add ( member . GetMemberPath ( ) , Expression . Constant ( value , typeof ( TProp ) ) ) ;
5447 return this ;
5548 }
5649
@@ -63,85 +56,5 @@ private static MemberExpression GetMemberExpression<TProp>(Expression<Func<TTarg
6356 throw new ArgumentException ( $ "The property expression must refer to a property of { param . Name } ({ param . Type . Name } )", nameof ( property ) ) ;
6457 return member ;
6558 }
66-
67- /// <summary>
68- /// Converts the assignments into a lambda expression, which creates a Dictionary<string,object%gt;.
69- /// </summary>
70- /// <returns>A lambda expression representing the assignments.</returns>
71- public LambdaExpression ConvertToDictionaryExpression ( )
72- {
73- var param = Expression . Parameter ( typeof ( TSource ) ) ;
74- var inits = new List < ElementInit > ( ) ;
75- foreach ( var set in _sets )
76- {
77- var setter = set . Expression ;
78- if ( setter is LambdaExpression setterLambda )
79- {
80- setter = setterLambda . Body . Replace ( setterLambda . Parameters . First ( ) , param ) ;
81- }
82- inits . Add ( Expression . ElementInit ( DictionaryAddMethodInfo , Expression . Constant ( set . PropertyPath ) ,
83- Expression . Convert ( setter , typeof ( object ) ) ) ) ;
84- }
85-
86- //The ListInit is intentionally "infected" with the lambda parameter (param), in the form of an IIF.
87- //The only relevance is to make sure that the ListInit is not evaluated by the PartialEvaluatingExpressionTreeVisitor,
88- //which could turn it into a Constant
89- var listInit = Expression . ListInit (
90- Expression . New (
91- DictionaryConstructorInfo ,
92- Expression . Condition (
93- Expression . Equal ( param , Expression . Constant ( null , typeof ( TSource ) ) ) ,
94- Expression . Constant ( _sets . Count ) ,
95- Expression . Constant ( _sets . Count ) ) ) ,
96- inits ) ;
97-
98- return Expression . Lambda ( listInit , param ) ;
99- }
100-
101- /// <summary>
102- /// Converts a members initialization expression to assignments. Unset members are ignored and left untouched.
103- /// </summary>
104- /// <param name="expression">The expression to convert.</param>
105- /// <returns>The corresponding assignments.</returns>
106- public static Assignments < TSource , TTarget > FromExpression ( Expression < Func < TSource , TTarget > > expression )
107- {
108- if ( expression == null )
109- throw new ArgumentNullException ( nameof ( expression ) ) ;
110- var instance = new Assignments < TSource , TTarget > ( ) ;
111- var memberInitExpression = expression . Body as MemberInitExpression ??
112- throw new ArgumentException ( "The expression must be member initialization, e.g. x => new Dog { Name = x.Name, Age = x.Age + 5 }" ) ;
113-
114- AddSetsFromBindings ( memberInitExpression . Bindings , instance , "" , expression . Parameters ) ;
115-
116- return instance ;
117- }
118-
119- private static void AddSetsFromBindings ( IEnumerable < MemberBinding > bindings , Assignments < TSource , TTarget > instance , string path , ReadOnlyCollection < ParameterExpression > parameters )
120- {
121- foreach ( var binding in bindings )
122- {
123- if ( binding . BindingType == MemberBindingType . Assignment ) // {Property="Value"}
124- {
125- AddSetsFromAssignment ( ( MemberAssignment ) binding , instance , path + "." + binding . Member . Name , parameters ) ;
126- }
127- else if ( binding . BindingType == MemberBindingType . MemberBinding ) // {Property={SubProperty="Value}}
128- {
129- AddSetsFromBindings ( ( ( MemberMemberBinding ) binding ) . Bindings , instance , path + "." + binding . Member . Name , parameters ) ;
130- }
131- }
132- }
133-
134- private static void AddSetsFromAssignment ( MemberAssignment assignment , Assignments < TSource , TTarget > instance , string path , ReadOnlyCollection < ParameterExpression > parameters )
135- {
136- // {Property=new Instance{SubProperty="Value"}}
137- if ( assignment . Expression is MemberInitExpression memberInit )
138- {
139- AddSetsFromBindings ( memberInit . Bindings , instance , path , parameters ) ;
140- }
141- else
142- {
143- instance . _sets . Add ( new Assignment ( path . Substring ( 1 ) , Expression . Lambda ( assignment . Expression , parameters ) ) ) ;
144- }
145- }
14659 }
14760}
0 commit comments