网站以上面的方式运行了一段时间,速度很好。但是,心中隐隐有所不甘,本来 Lambda 表达式就是为了方便,如果都这样写,反倒要费事了。既然我能自己构建表达式,为什么不能改造已有的表达式呢?于是经过各种研究,反复试验,终于出炉了我觉得比较满意的一个表达式转换类。当用户在调用查询时,会使用这个类重建一个表达式,把里面的进程内变量替换成值。代码如下:
using System.Collections.Generic;
namespace System.Linq.Expressions
{
public class ExpressionRebuilder<T, Tk> where T : class
{
public Expression<Func<T, Tk>> Result;
public ExpressionRebuilder(Expression<Func<T, Tk>> predicate)
{
Result = Expression.Lambda(Rebuild(predicate.Body), predicate.Parameters) as Expression<Func<T, Tk>>;
}
private Expression Rebuild(Expression exp)
{
if (exp is BinaryExpression)
return Rebuild((BinaryExpression)exp);
else if (exp is MethodCallExpression)
return Rebuild((MethodCallExpression)exp);
else if (exp is ConstantExpression)
return exp;
else if (exp is UnaryExpression)
return Rebuild((UnaryExpression)exp);
else if (exp is MemberExpression)
return Rebuild((MemberExpression)exp);
else if (exp is ConditionalExpression)
return Rebuild((ConditionalExpression)exp);
else
return exp;
}
private Expression Rebuild(UnaryExpression @Unary)
{
ExpressionType nt = @Unary.NodeType;
Expression op = @Unary.Operand;
if (nt == ExpressionType.Not)
{
op = Rebuild(op);
return Expression.Not(op);
}
else if (nt == ExpressionType.Convert)
{
if (op is MemberExpression)
{
op = Rebuild((MemberExpression)op);
}
if (op is ConstantExpression)
{
op = Expression.Convert(op, @Unary.Type);
op = Expression.Constant(Expression.Lambda(op).Compile().DynamicInvoke(), @Unary.Type);
return op;
}
else
return Expression.Convert(op, @Unary.Type);
}
else
return @Unary;
}
private Expression Rebuild(MethodCallExpression @MethodCall)
{
var a = @MethodCall.Arguments;
var rstArguments = new List<Expression>();
var obj = @MethodCall.Object;
if (obj != null)
{
if (obj.Type == typeof(System.Web.HttpRequest) && @MethodCall.Method.Name == "get_Item")
{
System.Web.HttpRequest swhr = (System.Web.HttpRequest)(((ConstantExpression)Rebuild((MemberExpression)obj)).Value);
object o = ((ConstantExpression)a[0]).Value;
string ag = swhr[o.ToString()];
return Expression.Constant(ag, typeof(string));
}
obj = Rebuild(obj);
}
foreach (var aa in a)
{
var bb = Rebuild(aa);
rstArguments.Add(bb);
}
MethodCallExpression mce;
if (obj == null)
{
mce = Expression.Call(@MethodCall.Method, rstArguments);
}
else
{
mce = Expression.Call(obj, @MethodCall.Method, rstArguments);
}
return ReduceMethodCallExpression(mce);
}
Expression ReduceMethodCallExpression(MethodCallExpression @MethodCall)
{
object o = @MethodCall.Object;
if (o != null && !(o is ConstantExpression))
return @MethodCall;
bool allTrue = true;
foreach (Expression a in @MethodCall.Arguments)
{
if (!(a is ConstantExpression))
{
allTrue = false;
break;
}
}
if (!allTrue)
return @MethodCall;
List<object> lo = new List<object>();
foreach (Expression a in @MethodCall.Arguments)
{
lo.Add(((ConstantExpression)a).Value);
}
object rsst = @MethodCall.Method.Invoke(o == null ? o : ((ConstantExpression)o).Value, lo.ToArray());
return Expression.Constant(rsst);
}
private Expression Rebuild(BinaryExpression @Binary)
{
Expression l = @Binary.Left;
Expression r = @Binary.Right;
l = Rebuild(l);
r = Rebuild(r);
if (l is ConstantExpression && r is ConstantExpression)
{
if (@Binary.NodeType == ExpressionType.ArrayIndex)
{
return Expression.Constant(((object[])(((ConstantExpression)l).Value))[(int)((ConstantExpression)r).Value]);
}
}
if (@Binary.NodeType == ExpressionType.AndAlso)
{
if (l is ConstantExpression)
{
var lc = (ConstantExpression)l;
if (lc.Type == typeof(bool))
{
bool lcb = (bool)lc.Value;
if (lcb)
return r;
else
return Expression.Constant(false, typeof(bool));
}
}
if (r is ConstantExpression)
{
var rc = (ConstantExpression)r;
if (rc.Type == typeof(bool))
{
bool rcb = (bool)rc.Value;
if (rcb)
return l;
else
return Expression.Constant(false, typeof(bool));
}
}
}
BinaryExpression be = Expression.MakeBinary(@Binary.NodeType, l, r, @Binary.IsLiftedToNull, @Binary.Method);
if (l is ConstantExpression && r is ConstantExpression)
{
try
{
object o = Expression.Lambda(be).Compile().DynamicInvoke();
return Expression.Constant(o, be.Type);
}
catch (Exception) { }
}
return be;
}
private Expression Rebuild(ConditionalExpression Ce)
{
Expression newTest = Rebuild(Ce.Test);
Expression newIfTrue = Rebuild(Ce.IfTrue);
Expression newIfFalse = Rebuild(Ce.IfFalse);
if (newTest is ConstantExpression)
{
ConstantExpression nt = (ConstantExpression)newTest;
if (nt.Type == typeof(bool))
{
bool b = (bool)nt.Value;
if (b)
return newIfTrue;
else
return newIfFalse;
}
}
return Expression.Condition(newTest, newIfTrue, newIfFalse);
}
private Expression Rebuild(MemberExpression Me)
{
if (Me.Expression != null && Me.Expression.NodeType == ExpressionType.Parameter)
{
return Me;
}
var rst = Me.Expression;
if (rst is MemberExpression)
{
rst = Rebuild((MemberExpression)rst);
}
try
{
object o = Expression.Lambda(Me).Compile().DynamicInvoke();
return Expression.Constant(o, Me.Type);
}
catch (Exception) { }
//下面的内容是先写的,有时会报错,后来使用了上边的DynamicInvoke()
if (!(rst is ConstantExpression))
return Me;
var b = ((ConstantExpression)rst).Value;
System.Reflection.FieldInfo fi;
fi = b.GetType().GetField(Me.Member.Name, System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic);
if (fi == null)
fi = b.GetType().GetField(Me.Member.Name);
if (fi != null)
{
object c = fi.GetValue(b);
ConstantExpression ce = Expression.Constant(c, Me.Type);
return ce;
}
else
{
System.Reflection.PropertyInfo pi = b.GetType().GetProperty(Me.Member.Name);
if (pi == null)
pi = b.GetType().GetProperty(Me.Member.Name, System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
object c = pi.GetValue(b);
ConstantExpression ce = Expression.Constant(c, Me.Type);
return ce;
}
}
}
}
调用时这样:
predicate = new System.Linq.Expressions.ExpressionRebuilder<Model.XXX, bool>(predicate).Result;
写到了扩展方法里:
public static Expression<Func<T,Tk>> Rebuild<T,Tk>(this Expression<Func<T, Tk>> predicate) where T:class
{
return new System.Linq.Expressions.ExpressionRebuilder<T, Tk>(predicate).Result;
}
在BLL.XXX里,只要这样一句就够了:
public List<T> GetList<Tm>(int pageSize, int pageIndex, Expression<Func<T, bool>> Efma, Expression<Func<T, Tm>> orderBy, bool desc, out int recordCount)
{
Efma = Efma.Rebuild();
return WCF.GetList(pageSize, pageIndex, Efmas, keySelector, desc, out recordCount);
}
这几年中,网站扩大了,手下多了几个人。他们调用数据服务时,和调用进程内的数据没有什么区别,再也不用写很长的代码,对他们来说是透明的。
最终实现了类似内存数据库的东西,速度比数据库要快几倍,并发性能也要高很多,因为是直接从内存检索数据。
这个类是我第一个比较满意的作品,如果有什么问题和意见,欢迎评论。