comparison Source/Data/Linq/Builder/ConcatUnionBuilder.cs @ 0:f990fcb411a9

Копия текущей версии из github
author cin
date Thu, 27 Mar 2014 21:46:09 +0400
parents
children
comparison
equal deleted inserted replaced
-1:000000000000 0:f990fcb411a9
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 }