Mercurial > pub > bltoolkit
comparison Source/Data/Linq/Builder/SelectBuilder.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; | |
| 3 using System.Collections.Generic; | |
| 4 using System.Data; | |
| 5 using System.Linq; | |
| 6 using System.Linq.Expressions; | |
| 7 | |
| 8 namespace BLToolkit.Data.Linq.Builder | |
| 9 { | |
| 10 using BLToolkit.Linq; | |
| 11 using Reflection; | |
| 12 | |
| 13 class SelectBuilder : MethodCallBuilder | |
| 14 { | |
| 15 #region SelectBuilder | |
| 16 | |
| 17 protected override bool CanBuildMethodCall(ExpressionBuilder builder, MethodCallExpression methodCall, BuildInfo buildInfo) | |
| 18 { | |
| 19 if (methodCall.IsQueryable("Select")) | |
| 20 { | |
| 21 switch (((LambdaExpression)methodCall.Arguments[1].Unwrap()).Parameters.Count) | |
| 22 { | |
| 23 case 1 : | |
| 24 case 2 : return true; | |
| 25 default: break; | |
| 26 } | |
| 27 } | |
| 28 | |
| 29 return false; | |
| 30 } | |
| 31 | |
| 32 protected override IBuildContext BuildMethodCall(ExpressionBuilder builder, MethodCallExpression methodCall, BuildInfo buildInfo) | |
| 33 { | |
| 34 var selector = (LambdaExpression)methodCall.Arguments[1].Unwrap(); | |
| 35 var sequence = builder.BuildSequence(new BuildInfo(buildInfo, methodCall.Arguments[0])); | |
| 36 | |
| 37 sequence.SetAlias(selector.Parameters[0].Name); | |
| 38 | |
| 39 var body = selector.Body.Unwrap(); | |
| 40 | |
| 41 switch (body.NodeType) | |
| 42 { | |
| 43 case ExpressionType.Parameter : break; | |
| 44 default : | |
| 45 sequence = CheckSubQueryForSelect(sequence); | |
| 46 break; | |
| 47 } | |
| 48 | |
| 49 var context = selector.Parameters.Count == 1 ? | |
| 50 new SelectContext (buildInfo.Parent, selector, sequence) : | |
| 51 new SelectContext2(buildInfo.Parent, selector, sequence); | |
| 52 | |
| 53 #if DEBUG | |
| 54 context.MethodCall = methodCall; | |
| 55 #endif | |
| 56 | |
| 57 return context; | |
| 58 } | |
| 59 | |
| 60 static IBuildContext CheckSubQueryForSelect(IBuildContext context) | |
| 61 { | |
| 62 return context.SqlQuery.Select.IsDistinct ? new SubQueryContext(context) : context; | |
| 63 } | |
| 64 | |
| 65 #endregion | |
| 66 | |
| 67 #region SelectContext2 | |
| 68 | |
| 69 class SelectContext2 : SelectContext | |
| 70 { | |
| 71 public SelectContext2(IBuildContext parent, LambdaExpression lambda, IBuildContext sequence) | |
| 72 : base(parent, lambda, sequence) | |
| 73 { | |
| 74 } | |
| 75 | |
| 76 static readonly ParameterExpression _counterParam = Expression.Parameter(typeof(int), "counter"); | |
| 77 | |
| 78 public override void BuildQuery<T>(Query<T> query, ParameterExpression queryParameter) | |
| 79 { | |
| 80 var expr = BuildExpression(null, 0); | |
| 81 | |
| 82 if (expr.Type != typeof(T)) | |
| 83 expr = Expression.Convert(expr, typeof(T)); | |
| 84 | |
| 85 var mapper = Expression.Lambda<Func<int,QueryContext,IDataContext,IDataReader,Expression,object[],T>>( | |
| 86 Builder.BuildBlock(expr), new [] | |
| 87 { | |
| 88 _counterParam, | |
| 89 ExpressionBuilder.ContextParam, | |
| 90 ExpressionBuilder.DataContextParam, | |
| 91 ExpressionBuilder.DataReaderParam, | |
| 92 ExpressionBuilder.ExpressionParam, | |
| 93 ExpressionBuilder.ParametersParam, | |
| 94 }); | |
| 95 | |
| 96 var func = mapper.Compile(); | |
| 97 | |
| 98 Func<QueryContext,IDataContext,IDataReader,Expression,object[],int,T> map = (ctx,db,rd,e,ps,n) => func(n, ctx, db, rd, e, ps); | |
| 99 | |
| 100 query.SetQuery(map); | |
| 101 } | |
| 102 | |
| 103 public override IsExpressionResult IsExpression(Expression expression, int level, RequestFor requestFlag) | |
| 104 { | |
| 105 switch (requestFlag) | |
| 106 { | |
| 107 case RequestFor.Expression : | |
| 108 case RequestFor.Root : | |
| 109 if (expression == Lambda.Parameters[1]) | |
| 110 return IsExpressionResult.True; | |
| 111 break; | |
| 112 } | |
| 113 | |
| 114 return base.IsExpression(expression, level, requestFlag); | |
| 115 } | |
| 116 | |
| 117 public override Expression BuildExpression(Expression expression, int level) | |
| 118 { | |
| 119 if (expression == Lambda.Parameters[1]) | |
| 120 return _counterParam; | |
| 121 | |
| 122 return base.BuildExpression(expression, level); | |
| 123 } | |
| 124 } | |
| 125 | |
| 126 #endregion | |
| 127 | |
| 128 #region Convert | |
| 129 | |
| 130 protected override SequenceConvertInfo Convert( | |
| 131 ExpressionBuilder builder, MethodCallExpression originalMethodCall, BuildInfo buildInfo, ParameterExpression param) | |
| 132 { | |
| 133 var methodCall = originalMethodCall; | |
| 134 var selector = (LambdaExpression)methodCall.Arguments[1].Unwrap(); | |
| 135 var info = builder.ConvertSequence(new BuildInfo(buildInfo, methodCall.Arguments[0]), selector.Parameters[0]); | |
| 136 | |
| 137 if (info != null) | |
| 138 { | |
| 139 methodCall = (MethodCallExpression)methodCall.Convert( | |
| 140 ex => ConvertMethod(methodCall, 0, info, selector.Parameters[0], ex)); | |
| 141 selector = (LambdaExpression)methodCall.Arguments[1].Unwrap(); | |
| 142 } | |
| 143 | |
| 144 if (param != null && param != builder.SequenceParameter) | |
| 145 { | |
| 146 var list = | |
| 147 ( | |
| 148 from path in GetExpressions(selector.Parameters[0], param, 0, selector.Body.Unwrap()) | |
| 149 orderby path.Level descending | |
| 150 select path | |
| 151 ).ToList(); | |
| 152 | |
| 153 if (list.Count > 0) | |
| 154 { | |
| 155 var plist = list.Where(e => e.Expr == selector.Parameters[0]).ToList(); | |
| 156 | |
| 157 if (plist.Count > 1) | |
| 158 list = list.Except(plist.Skip(1)).ToList(); | |
| 159 | |
| 160 var p = plist.FirstOrDefault(); | |
| 161 | |
| 162 if (p == null) | |
| 163 { | |
| 164 var types = methodCall.Method.GetGenericArguments(); | |
| 165 var mgen = methodCall.Method.GetGenericMethodDefinition(); | |
| 166 var btype = typeof(ExpressionHoder<,>).MakeGenericType(types[0], selector.Body.Type); | |
| 167 var fields = btype.GetFields(); | |
| 168 var pold = selector.Parameters[0]; | |
| 169 var psel = Expression.Parameter(types[0], pold.Name); | |
| 170 | |
| 171 methodCall = Expression.Call( | |
| 172 methodCall.Object, | |
| 173 mgen.MakeGenericMethod(types[0], btype), | |
| 174 methodCall.Arguments[0], | |
| 175 Expression.Lambda( | |
| 176 Expression.MemberInit( | |
| 177 Expression.New(btype), | |
| 178 Expression.Bind(fields[0], psel), | |
| 179 Expression.Bind(fields[1], selector.Body.Convert(e => e == pold ? psel : e))), | |
| 180 psel)); | |
| 181 | |
| 182 selector = (LambdaExpression)methodCall.Arguments[1].Unwrap(); | |
| 183 param = Expression.Parameter(selector.Body.Type, param.Name); | |
| 184 | |
| 185 list.Add(new SequenceConvertPath { Path = param, Expr = Expression.MakeMemberAccess(param, fields[1]), Level = 1 }); | |
| 186 | |
| 187 var expr = Expression.MakeMemberAccess(param, fields[0]); | |
| 188 | |
| 189 foreach (var t in list) | |
| 190 t.Expr = t.Expr.Convert(ex => ex == pold ? expr : ex); | |
| 191 | |
| 192 return new SequenceConvertInfo | |
| 193 { | |
| 194 Parameter = param, | |
| 195 Expression = methodCall, | |
| 196 ExpressionsToReplace = list | |
| 197 }; | |
| 198 } | |
| 199 | |
| 200 if (info != null) | |
| 201 { | |
| 202 if (info.ExpressionsToReplace != null) | |
| 203 { | |
| 204 foreach (var path in info.ExpressionsToReplace) | |
| 205 { | |
| 206 path.Path = path.Path.Convert(e => e == info.Parameter ? p.Path : e); | |
| 207 path.Expr = path.Expr.Convert(e => e == info.Parameter ? p.Path : e); | |
| 208 path.Level += p.Level; | |
| 209 | |
| 210 list.Add(path); | |
| 211 } | |
| 212 | |
| 213 list = list.OrderByDescending(path => path.Level).ToList(); | |
| 214 } | |
| 215 } | |
| 216 | |
| 217 if (list.Count > 1) | |
| 218 { | |
| 219 return new SequenceConvertInfo | |
| 220 { | |
| 221 Parameter = param, | |
| 222 Expression = methodCall, | |
| 223 ExpressionsToReplace = list | |
| 224 .Where (e => e != p) | |
| 225 .Select(ei => | |
| 226 { | |
| 227 ei.Expr = ei.Expr.Convert(e => e == p.Expr ? p.Path : e); | |
| 228 return ei; | |
| 229 }) | |
| 230 .ToList() | |
| 231 }; | |
| 232 } | |
| 233 } | |
| 234 } | |
| 235 | |
| 236 if (methodCall != originalMethodCall) | |
| 237 return new SequenceConvertInfo | |
| 238 { | |
| 239 Parameter = param, | |
| 240 Expression = methodCall, | |
| 241 }; | |
| 242 | |
| 243 return null; | |
| 244 } | |
| 245 | |
| 246 static IEnumerable<SequenceConvertPath> GetExpressions(ParameterExpression param, Expression path, int level, Expression expression) | |
| 247 { | |
| 248 switch (expression.NodeType) | |
| 249 { | |
| 250 // new { ... } | |
| 251 // | |
| 252 case ExpressionType.New : | |
| 253 { | |
| 254 var expr = (NewExpression)expression; | |
| 255 | |
| 256 if (expr.Members != null) for (var i = 0; i < expr.Members.Count; i++) | |
| 257 { | |
| 258 var q = GetExpressions(param, Expression.MakeMemberAccess(path, expr.Members[i]), level + 1, expr.Arguments[i]); | |
| 259 foreach (var e in q) | |
| 260 yield return e; | |
| 261 } | |
| 262 | |
| 263 break; | |
| 264 } | |
| 265 | |
| 266 // new MyObject { ... } | |
| 267 // | |
| 268 case ExpressionType.MemberInit : | |
| 269 { | |
| 270 var expr = (MemberInitExpression)expression; | |
| 271 var dic = TypeAccessor.GetAccessor(expr.Type) | |
| 272 .Select((m,i) => new { m, i }) | |
| 273 .ToDictionary(_ => _.m.MemberInfo.Name, _ => _.i); | |
| 274 | |
| 275 foreach (var binding in expr.Bindings.Cast<MemberAssignment>().OrderBy(b => dic[b.Member.Name])) | |
| 276 { | |
| 277 var q = GetExpressions(param, Expression.MakeMemberAccess(path, binding.Member), level + 1, binding.Expression); | |
| 278 foreach (var e in q) | |
| 279 yield return e; | |
| 280 } | |
| 281 | |
| 282 break; | |
| 283 } | |
| 284 | |
| 285 // parameter | |
| 286 // | |
| 287 case ExpressionType.Parameter : | |
| 288 if (expression == param) | |
| 289 yield return new SequenceConvertPath { Path = path, Expr = expression, Level = level }; | |
| 290 break; | |
| 291 | |
| 292 case ExpressionType.TypeAs : | |
| 293 yield return new SequenceConvertPath { Path = path, Expr = expression, Level = level }; | |
| 294 break; | |
| 295 | |
| 296 // Queriable method. | |
| 297 // | |
| 298 case ExpressionType.Call : | |
| 299 { | |
| 300 var call = (MethodCallExpression)expression; | |
| 301 | |
| 302 if (call.IsQueryable()) | |
| 303 if (TypeHelper.IsSameOrParent(typeof(IEnumerable), call.Type) || | |
| 304 TypeHelper.IsSameOrParent(typeof(IQueryable), call.Type) || | |
| 305 FirstSingleBuilder.MethodNames.Contains(call.Method.Name)) | |
| 306 yield return new SequenceConvertPath { Path = path, Expr = expression, Level = level }; | |
| 307 | |
| 308 break; | |
| 309 } | |
| 310 } | |
| 311 } | |
| 312 | |
| 313 #endregion | |
| 314 } | |
| 315 } |
