Mercurial > pub > bltoolkit
diff Source/Data/Linq/Builder/TakeSkipBuilder.cs @ 0:f990fcb411a9
Копия текущей версии из github
author | cin |
---|---|
date | Thu, 27 Mar 2014 21:46:09 +0400 |
parents | |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Source/Data/Linq/Builder/TakeSkipBuilder.cs Thu Mar 27 21:46:09 2014 +0400 @@ -0,0 +1,139 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; + +namespace BLToolkit.Data.Linq.Builder +{ + using BLToolkit.Linq; + using Data.Sql; + + class TakeSkipBuilder : MethodCallBuilder + { + protected override bool CanBuildMethodCall(ExpressionBuilder builder, MethodCallExpression methodCall, BuildInfo buildInfo) + { + return methodCall.IsQueryable("Skip", "Take"); + } + + protected override IBuildContext BuildMethodCall(ExpressionBuilder builder, MethodCallExpression methodCall, BuildInfo buildInfo) + { + var sequence = builder.BuildSequence(new BuildInfo(buildInfo, methodCall.Arguments[0])); + + var arg = methodCall.Arguments[1].Unwrap(); + + if (arg.NodeType == ExpressionType.Lambda) + arg = ((LambdaExpression)arg).Body.Unwrap(); + + var expr = builder.ConvertToSql(sequence, arg, false); + + if (methodCall.Method.Name == "Take") + { + BuildTake(builder, sequence, expr); + } + else + { + BuildSkip(builder, sequence, sequence.SqlQuery.Select.SkipValue, expr); + } + + return sequence; + } + + protected override SequenceConvertInfo Convert( + ExpressionBuilder builder, MethodCallExpression methodCall, BuildInfo buildInfo, ParameterExpression param) + { + var info = builder.ConvertSequence(new BuildInfo(buildInfo, methodCall.Arguments[0]), null); + + if (info != null) + { + info.Expression = + Expression.Call( + methodCall.Method.DeclaringType, + methodCall.Method.Name, + new[] { info.Expression.Type.GetGenericArguments()[0] }, + info.Expression, methodCall.Arguments[1]); + info.Parameter = param; + + return info; + } + + return null; + } + + static void BuildTake(ExpressionBuilder builder, IBuildContext sequence, ISqlExpression expr) + { + var sql = sequence.SqlQuery; + + builder.SqlProvider.SqlQuery = sql; + + sql.Select.Take(expr); + + if (sql.Select.SkipValue != null && builder.SqlProvider.IsTakeSupported && !builder.SqlProvider.IsSkipSupported) + { + if (sql.Select.SkipValue is SqlParameter && sql.Select.TakeValue is SqlValue) + { + var skip = (SqlParameter)sql.Select.SkipValue; + var parm = (SqlParameter)sql.Select.SkipValue.Clone(new Dictionary<ICloneableElement,ICloneableElement>(), _ => true); + + parm.SetTakeConverter((int)((SqlValue)sql.Select.TakeValue).Value); + + sql.Select.Take(parm); + + var ep = (from pm in builder.CurrentSqlParameters where pm.SqlParameter == skip select pm).First(); + + ep = new ParameterAccessor + { + Expression = ep.Expression, + Accessor = ep.Accessor, + SqlParameter = parm + }; + + builder.CurrentSqlParameters.Add(ep); + } + else + sql.Select.Take(builder.Convert( + sequence, + new SqlBinaryExpression(typeof(int), sql.Select.SkipValue, "+", sql.Select.TakeValue, Precedence.Additive))); + } + + if (!builder.SqlProvider.TakeAcceptsParameter) + { + var p = sql.Select.TakeValue as SqlParameter; + + if (p != null) + p.IsQueryParameter = false; + } + } + + static void BuildSkip(ExpressionBuilder builder, IBuildContext sequence, ISqlExpression prevSkipValue, ISqlExpression expr) + { + var sql = sequence.SqlQuery; + + builder.SqlProvider.SqlQuery = sql; + + sql.Select.Skip(expr); + + builder.SqlProvider.SqlQuery = sql; + + if (sql.Select.TakeValue != null) + { + if (builder.SqlProvider.IsSkipSupported || !builder.SqlProvider.IsTakeSupported) + sql.Select.Take(builder.Convert( + sequence, + new SqlBinaryExpression(typeof(int), sql.Select.TakeValue, "-", sql.Select.SkipValue, Precedence.Additive))); + + if (prevSkipValue != null) + sql.Select.Skip(builder.Convert( + sequence, + new SqlBinaryExpression(typeof(int), prevSkipValue, "+", sql.Select.SkipValue, Precedence.Additive))); + } + + if (!builder.SqlProvider.TakeAcceptsParameter) + { + var p = sql.Select.SkipValue as SqlParameter; + + if (p != null) + p.IsQueryParameter = false; + } + } + } +}