Mercurial > pub > bltoolkit
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 } |