0
|
1 using System;
|
|
2 using System.Collections.Generic;
|
|
3 using System.Linq;
|
|
4 using System.Linq.Expressions;
|
|
5 using System.Reflection;
|
|
6
|
|
7 namespace BLToolkit.Data.Linq.Builder
|
|
8 {
|
|
9 using BLToolkit.Linq;
|
|
10 using Data.Sql;
|
|
11 using Reflection;
|
|
12
|
|
13 class ConcatUnionBuilder : MethodCallBuilder
|
|
14 {
|
|
15 #region Builder
|
|
16
|
|
17 protected override bool CanBuildMethodCall(ExpressionBuilder builder, MethodCallExpression methodCall, BuildInfo buildInfo)
|
|
18 {
|
|
19 return methodCall.Arguments.Count == 2 && methodCall.IsQueryable("Concat", "Union");
|
|
20 }
|
|
21
|
|
22 protected override IBuildContext BuildMethodCall(ExpressionBuilder builder, MethodCallExpression methodCall, BuildInfo buildInfo)
|
|
23 {
|
|
24 var sequence1 = new SubQueryContext(builder.BuildSequence(new BuildInfo(buildInfo, methodCall.Arguments[0])));
|
|
25 var sequence2 = new SubQueryContext(builder.BuildSequence(new BuildInfo(buildInfo, methodCall.Arguments[1], new SqlQuery())));
|
|
26 var union = new SqlQuery.Union(sequence2.SqlQuery, methodCall.Method.Name == "Concat");
|
|
27
|
|
28 sequence1.SqlQuery.Unions.Add(union);
|
|
29
|
|
30 return new UnionContext(sequence1, sequence2, methodCall);
|
|
31 }
|
|
32
|
|
33 protected override SequenceConvertInfo Convert(
|
|
34 ExpressionBuilder builder, MethodCallExpression methodCall, BuildInfo buildInfo, ParameterExpression param)
|
|
35 {
|
|
36 return null;
|
|
37 }
|
|
38
|
|
39 #endregion
|
|
40
|
|
41 #region Context
|
|
42
|
|
43 sealed class UnionContext : SubQueryContext
|
|
44 {
|
|
45 public UnionContext(SubQueryContext sequence1, SubQueryContext sequence2, MethodCallExpression methodCall)
|
|
46 : base(sequence1)
|
|
47 {
|
|
48 _methodCall = methodCall;
|
|
49
|
|
50 _isObject =
|
|
51 sequence1.IsExpression(null, 0, RequestFor.Object).Result ||
|
|
52 sequence2.IsExpression(null, 0, RequestFor.Object).Result;
|
|
53
|
|
54 if (_isObject)
|
|
55 {
|
|
56 var type = _methodCall.Method.GetGenericArguments()[0];
|
|
57 _unionParameter = Expression.Parameter(type, "t");
|
|
58 }
|
|
59
|
|
60 Init(sequence1, sequence2);
|
|
61 }
|
|
62
|
|
63 readonly bool _isObject;
|
|
64 readonly MethodCallExpression _methodCall;
|
|
65 readonly ParameterExpression _unionParameter;
|
|
66 readonly Dictionary<MemberInfo,Member> _members = new Dictionary<MemberInfo,Member>(new MemberInfoComparer());
|
|
67
|
|
68 class Member
|
|
69 {
|
|
70 public SqlInfo SequenceInfo;
|
|
71 public SqlInfo SqlQueryInfo;
|
|
72 public MemberExpression MemberExpression;
|
|
73 }
|
|
74
|
|
75 class UnionMember
|
|
76 {
|
|
77 public Member Member;
|
|
78 public SqlInfo Info1;
|
|
79 public SqlInfo Info2;
|
|
80 }
|
|
81
|
|
82 void Init(SubQueryContext sequence1, SubQueryContext sequence2)
|
|
83 {
|
|
84 var info1 = sequence1.ConvertToIndex(null, 0, ConvertFlags.All).ToList();
|
|
85 var info2 = sequence2.ConvertToIndex(null, 0, ConvertFlags.All).ToList();
|
|
86
|
|
87 if (!_isObject)
|
|
88 return;
|
|
89
|
|
90 var members = new List<UnionMember>();
|
|
91
|
|
92 foreach (var info in info1)
|
|
93 {
|
|
94 if (info.Members.Count == 0)
|
|
95 throw new InvalidOperationException();
|
|
96
|
|
97 var member = new Member
|
|
98 {
|
|
99 SequenceInfo = info,
|
|
100 MemberExpression = Expression.MakeMemberAccess(_unionParameter, info.Members[0])
|
|
101 };
|
|
102
|
|
103 members.Add(new UnionMember { Member = member, Info1 = info });
|
|
104 }
|
|
105
|
|
106 foreach (var info in info2)
|
|
107 {
|
|
108 if (info.Members.Count == 0)
|
|
109 throw new InvalidOperationException();
|
|
110
|
|
111 var em = members.FirstOrDefault(m =>
|
|
112 m.Member.SequenceInfo != null &&
|
|
113 m.Member.SequenceInfo.CompareLastMember(info));
|
|
114
|
|
115 if (em == null)
|
|
116 {
|
|
117 var member = new Member { MemberExpression = Expression.MakeMemberAccess(_unionParameter, info.Members[0]) };
|
|
118
|
|
119 if (sequence2.IsExpression(member.MemberExpression, 1, RequestFor.Object).Result)
|
|
120 throw new LinqException("Types in {0} are constructed incompatibly.", _methodCall.Method.Name);
|
|
121
|
|
122 members.Add(new UnionMember { Member = member, Info2 = info });
|
|
123 }
|
|
124 else
|
|
125 {
|
|
126 em.Info2 = info;
|
|
127 }
|
|
128 }
|
|
129
|
|
130 sequence1.SqlQuery.Select.Columns.Clear();
|
|
131 sequence2.SqlQuery.Select.Columns.Clear();
|
|
132
|
|
133 for (var i = 0; i < members.Count; i++)
|
|
134 {
|
|
135 var member = members[i];
|
|
136
|
|
137 if (member.Info1 == null)
|
|
138 {
|
|
139 member.Info1 = new SqlInfo(member.Info2.Members)
|
|
140 {
|
|
141 Sql = new SqlValue(null),
|
|
142 Query = sequence1.SqlQuery,
|
|
143 };
|
|
144
|
|
145 member.Member.SequenceInfo = member.Info1;
|
|
146 }
|
|
147
|
|
148 if (member.Info2 == null)
|
|
149 {
|
|
150 member.Info2 = new SqlInfo(member.Info1.Members)
|
|
151 {
|
|
152 Sql = new SqlValue(null),
|
|
153 Query = sequence2.SqlQuery,
|
|
154 };
|
|
155 }
|
|
156
|
|
157 sequence1.SqlQuery.Select.Columns.Add(new SqlQuery.Column(sequence1.SqlQuery, member.Info1.Sql));
|
|
158 sequence2.SqlQuery.Select.Columns.Add(new SqlQuery.Column(sequence2.SqlQuery, member.Info2.Sql));
|
|
159
|
|
160 member.Member.SequenceInfo.Index = i;
|
|
161
|
|
162 _members[member.Member.MemberExpression.Member] = member.Member;
|
|
163 }
|
|
164
|
|
165 foreach (var key in sequence1.ColumnIndexes.Keys.ToList())
|
|
166 sequence1.ColumnIndexes[key] = sequence1.SqlQuery.Select.Add(key);
|
|
167
|
|
168 foreach (var key in sequence2.ColumnIndexes.Keys.ToList())
|
|
169 sequence2.ColumnIndexes[key] = sequence2.SqlQuery.Select.Add(key);
|
|
170 }
|
|
171
|
|
172 public override void BuildQuery<T>(Query<T> query, ParameterExpression queryParameter)
|
|
173 {
|
|
174 var expr = BuildExpression(null, 0);
|
|
175 var mapper = Builder.BuildMapper<T>(expr);
|
|
176
|
|
177 query.SetQuery(mapper.Compile());
|
|
178 }
|
|
179
|
|
180 public override Expression BuildExpression(Expression expression, int level)
|
|
181 {
|
|
182 if (_isObject)
|
|
183 {
|
|
184 if (expression == null)
|
|
185 {
|
|
186 var type = _methodCall.Method.GetGenericArguments()[0];
|
|
187 var nctor = (NewExpression)Expression.Find(e =>
|
|
188 {
|
|
189 if (e.NodeType == ExpressionType.New && e.Type == type)
|
|
190 {
|
|
191 var ne = (NewExpression)e;
|
|
192 return ne.Arguments != null && ne.Arguments.Count > 0;
|
|
193 }
|
|
194
|
|
195 return false;
|
|
196 });
|
|
197
|
|
198 Expression expr;
|
|
199
|
|
200 if (nctor != null)
|
|
201 {
|
|
202 var members = nctor.Members
|
|
203 .Select(m => m is MethodInfo ? TypeHelper.GetPropertyByMethod((MethodInfo)m) : m)
|
|
204 .ToList();
|
|
205
|
|
206 expr = Expression.New(
|
|
207 nctor.Constructor,
|
|
208 members
|
|
209 .Select(m => Expression.PropertyOrField(_unionParameter, m.Name))
|
|
210 .Cast<Expression>(),
|
|
211 members);
|
|
212 }
|
|
213 else
|
|
214 {
|
|
215 var ta = TypeAccessor.GetAccessor(type);
|
|
216
|
|
217 expr = Expression.MemberInit(
|
|
218 Expression.New(ta.Type),
|
|
219 _members
|
|
220 .Select(m => Expression.Bind(m.Value.MemberExpression.Member, m.Value.MemberExpression))
|
|
221 .Cast<MemberBinding>());
|
|
222 }
|
|
223
|
|
224 var ex = Builder.BuildExpression(this, expr);
|
|
225
|
|
226 return ex;
|
|
227 }
|
|
228
|
|
229 if (level == 0 || level == 1)
|
|
230 {
|
|
231 var levelExpression = expression.GetLevelExpression(1);
|
|
232
|
|
233 if (expression == levelExpression && !IsExpression(expression, 1, RequestFor.Object).Result)
|
|
234 {
|
|
235 var idx = ConvertToIndex(expression, level, ConvertFlags.Field);
|
|
236 var n = idx[0].Index;
|
|
237
|
|
238 if (Parent != null)
|
|
239 n = Parent.ConvertToParentIndex(n, this);
|
|
240
|
|
241 return Builder.BuildSql(expression.Type, n);
|
|
242 }
|
|
243 }
|
|
244 }
|
|
245
|
|
246 return base.BuildExpression(expression, level);
|
|
247 }
|
|
248
|
|
249 public override IsExpressionResult IsExpression(Expression expression, int level, RequestFor testFlag)
|
|
250 {
|
|
251 if (testFlag == RequestFor.Root && expression == _unionParameter)
|
|
252 return IsExpressionResult.True;
|
|
253
|
|
254 return base.IsExpression(expression, level, testFlag);
|
|
255 }
|
|
256
|
|
257 public override SqlInfo[] ConvertToIndex(Expression expression, int level, ConvertFlags flags)
|
|
258 {
|
|
259 if (_isObject)
|
|
260 {
|
|
261 return ConvertToSql(expression, level, flags)
|
|
262 .Select(idx =>
|
|
263 {
|
|
264 if (idx.Index < 0)
|
|
265 {
|
|
266 if (idx.Index == -2)
|
|
267 {
|
|
268 SqlQuery.Select.Columns.Add(new SqlQuery.Column(SqlQuery, idx.Sql));
|
|
269 idx.Index = SqlQuery.Select.Columns.Count - 1;
|
|
270 }
|
|
271 else
|
|
272 {
|
|
273 idx.Index = SqlQuery.Select.Add(idx.Sql);
|
|
274 }
|
|
275 }
|
|
276
|
|
277 return idx;
|
|
278 })
|
|
279 .ToArray();
|
|
280 }
|
|
281
|
|
282 return base.ConvertToIndex(expression, level, flags);
|
|
283 }
|
|
284
|
|
285 public override SqlInfo[] ConvertToSql(Expression expression, int level, ConvertFlags flags)
|
|
286 {
|
|
287 if (_isObject)
|
|
288 {
|
|
289 switch (flags)
|
|
290 {
|
|
291 case ConvertFlags.All :
|
|
292 case ConvertFlags.Key :
|
|
293
|
|
294 if (expression == null)
|
|
295 {
|
|
296 return _members.Values
|
|
297 .Select(m => ConvertToSql(m.MemberExpression, 0, ConvertFlags.Field)[0])
|
|
298 .ToArray();
|
|
299 }
|
|
300
|
|
301 break;
|
|
302
|
|
303 case ConvertFlags.Field :
|
|
304
|
|
305 if (expression != null && (level == 0 || level == 1) && expression.NodeType == ExpressionType.MemberAccess)
|
|
306 {
|
|
307 var levelExpression = expression.GetLevelExpression(1);
|
|
308
|
|
309 if (expression == levelExpression)
|
|
310 {
|
|
311 var ma = (MemberExpression)expression;
|
|
312 var member = _members[ma.Member];
|
|
313
|
|
314 if (member.SqlQueryInfo == null)
|
|
315 {
|
|
316 member.SqlQueryInfo = new SqlInfo(member.MemberExpression.Member)
|
|
317 {
|
|
318 Index = -2,
|
|
319 Sql = SubQuery.SqlQuery.Select.Columns[member.SequenceInfo.Index],
|
|
320 Query = SqlQuery,
|
|
321 };
|
|
322 }
|
|
323
|
|
324 return new[] { member.SqlQueryInfo };
|
|
325 }
|
|
326 }
|
|
327
|
|
328 break;
|
|
329 }
|
|
330
|
|
331 throw new InvalidOperationException();
|
|
332 }
|
|
333
|
|
334 return base.ConvertToSql(expression, level, flags);
|
|
335 }
|
|
336 }
|
|
337
|
|
338 #endregion
|
|
339 }
|
|
340 }
|