干货表达式树解析框架3.docx
- 文档编号:4343256
- 上传时间:2022-11-30
- 格式:DOCX
- 页数:20
- 大小:72.72KB
干货表达式树解析框架3.docx
《干货表达式树解析框架3.docx》由会员分享,可在线阅读,更多相关《干货表达式树解析框架3.docx(20页珍藏版)》请在冰豆网上搜索。
干货表达式树解析框架3
这应该是年前最后一篇了,接下来的时间就要陪陪老婆孩子了
关于表达式树解析也是最后一篇了,该说到的中心思想都已经说到了,理解接受能力比较好的童鞋应该已经可以举一反三了
研究表达式树的过程让我感觉微软的设计真的是非常的巧妙,也为今后我的开发之路增添了新的思路
好了废话不多说了这篇主要是为了解决上篇中的提问的
声明
解决问题的办法有很多,我只是根据我的个人习惯和风格介绍我的解决方案,并不一定就是最好的,仅仅只是提供一种思路,大家可以根据自己或项目的实际情况酌情对待
关于问题请参考干货!
表达式树解析"框架"
(2)结尾
问题一
db.Where
=null);//u.Nameisnotnull而非(u.Name<>null)
分析
这个问题主要是在Sql中`二元表达式`有一个非常特别的情况,如果和null进行比较,那么应该用is或isnot而不是=或者<>(!
=)
so~我的做法是在解析二元表达式的类中处理,如第二个参数是null,且符号是Equals或NotEqual,则使用is/isnot
怎么判断第二个参数是null?
这里我打算直接判断ParserArgs.Builder中最后5个字符,如果是"NULL"就算是NULL了
但是这里有个问题,就是原来的操作是先加入符号,再加入Right的,所以这里也要改,改为先放入Right再插入符号
代码如下
classBinaryExpressionParser:
ExpressionParser
{
publicoverridevoidWhere(BinaryExpressionexpr,ParserArgsargs)
{
if(ExistsBracket(expr.Left))
{
args.Builder.Append('');
args.Builder.Append('(');
Parser.Where(expr.Left,args);
args.Builder.Append(')');
}
else
{
Parser.Where(expr.Left,args);
}
varindex=args.Builder.Length;
if(ExistsBracket(expr.Right))
{
args.Builder.Append('');
args.Builder.Append('(');
Parser.Where(expr.Right,args);
args.Builder.Append(')');
}
else
{
Parser.Where(expr.Right,args);
}
varlength=args.Builder.Length;
if(length-index==5&&
args.Builder[length-5]==''&&
args.Builder[length-4]=='N'&&
args.Builder[length-3]=='U'&&
args.Builder[length-2]=='L'&&
args.Builder[length-1]=='L')
{
Sign(expr.NodeType,index,args,true);
}
else
{
Sign(expr.NodeType,index,args);
}
}
///
///
privatestaticboolExistsBracket(Expressionexpr)
{
vars=expr.ToString();
returns!
=null&&s.Length>5&&s[0]=='('&&s[1]=='(';
}
privatestaticvoidSign(ExpressionTypetype,intindex,ParserArgsargs,booluseis=false)
{
switch(type)
{
caseExpressionType.And:
caseExpressionType.AndAlso:
args.Builder.Insert(index,"AND");
break;
caseExpressionType.Equal:
if(useis)
{
args.Builder.Insert(index,"IS");
}
else
{
args.Builder.Insert(index,"=");
}
break;
caseExpressionType.GreaterThan:
args.Builder.Insert(index,">");
break;
caseExpressionType.GreaterThanOrEqual:
args.Builder.Insert(index,">=");
break;
caseExpressionType.NotEqual:
if(useis)
{
args.Builder.Insert(index,"ISNOT");
}
else
{
args.Builder.Insert(index,"<>");
}
break;
caseExpressionType.Or:
caseExpressionType.OrElse:
args.Builder.Insert(index,"OR");
break;
caseExpressionType.LessThan:
args.Builder.Insert(index,"<");
break;
caseExpressionType.LessThanOrEqual:
args.Builder.Insert(index,"<=");
break;
default:
thrownewNotImplementedException("无法解释节点类型"+type);
}
}
......
}
结果
db.Where
=null);
//打印SELECT*FROM[User]uWHEREu.[Name]ISNOTNULL
问题二
db.Where
分析
这2个表达式只要运行一下就可以知道,他们是无法被解析的,原因就是:
尚未实现MethodCallExpression的解析
因为2个都属性MethodCall表达式
所以只需要实现MethodCallExpressionParser即可
MethodCallExpression方法调用表达式
Method表示调用的方法
Arguments表示方法中用到的参数
Object表示调用方法的实例对象
每种方法对应的解析都是不同的,所以我为每个方法都实现一个单独的解析函数
比如String类中的3个操作,分别对应3种Like的情况
publicstaticvoidString_StartsWith(MethodCallExpressionexpr,ParserArgsargs)
{
}
publicstaticvoidString_Contains(MethodCallExpressionexpr,ParserArgsargs)
{
}
publicstaticvoidString_EndsWith(MethodCallExpressionexpr,ParserArgsargs)
{
}
然后将他们加入到一个键值对中(因为方法是有重载的,所以会有一样名字的方法,但是解析方式是相同的)
staticDictionary
privatestaticDictionary
{
Dictionary
vartype=typeof(string);
foreach(varmetintype.GetMethods())
{
switch(met.Name)
{
case"StartsWith":
dict.Add(met,String_StartsWith);
break;
case"Contains":
dict.Add(met,String_Contains);
break;
case"EndsWith":
dict.Add(met,String_EndsWith);
break;
default:
break;
}
}
returndict;
}
调用where
publicoverridevoidWhere(MethodCallExpressionexpr,ParserArgsargs)
{
Action
if(_Methods.TryGetValue(expr.Method,outact))
{
act(expr,args);
return;
}
thrownewNotImplementedException("无法解释方法"+expr.Method);
}
现在分别完成3个String_的函数就可以了
publicstaticvoidString_StartsWith(MethodCallExpressionexpr,ParserArgsargs)
{
Parser.Where(expr.Object,args);
args.Builder.Append("LIKE");
Parser.Where(expr.Arguments[0],args);
args.Builder.Append("+'%'");
}
publicstaticvoidString_Contains(MethodCallExpressionexpr,ParserArgsargs)
{
Parser.Where(expr.Object,args);
args.Builder.Append("LIKE'%'+");
Parser.Where(expr.Arguments[0],args);
args.Builder.Append("+'%'");
}
publicstaticvoidString_EndsWith(MethodCallExpressionexpr,ParserArgsargs)
{
Parser.Where(expr.Object,args);
args.Builder.Append("LIKE'%'+");
Parser.Where(expr.Arguments[0],args);
}
结果
db.Where
db.Where
db.Where
/*打印
SELECT*FROM[User]uWHEREu.[Name]LIKE'bl'+'%'
SELECT*FROM[User]uWHEREu.[Name]LIKE'%'+'bl'+'%'
SELECT*FROM[User]uWHEREu.[Name]LIKE'%'+'bl'
*/
问题三
int[]arr={13,15,17,19,21};
db.Where
分析
这个问题和刚才那个问题有很多相似之处,所以首先需要在MethodCallExpressionParser类中实现一个对应Enumerable.Contains的解析函数
但是这个方法有一个比较特殊的地方就是他是泛型方法,所以在从键值对中获取处理函数的时候,需要把他转为`泛型方法定义`(MethodInfo.GetGenericMethodDefinition)的才可以
publicoverridevoidWhere(MethodCallExpressionexpr,ParserArgsargs)
{
Action
varkey=expr.Method;
if(key.IsGenericMethod)
{
key=key.GetGenericMethodDefinition();
}
if(_Methods.TryGetValue(key,outact))
{
act(expr,args);
return;
}
thrownewNotImplementedException("无法解释方法"+expr.Method);
}
对应的处理函数
publicstaticvoidEnumerable_Contains(MethodCallExpressionexpr,ParserArgsargs)
{
Parser.Where(expr.Arguments[1],args);
args.Builder.Append("IN");
Parser.Where(expr.Arguments[0],args);
}
看上去似乎已经完成了,但是结果是....
int[]arr={13,15,17,19,21};
db.Where
//打印
//SELECT*FROM[User]uWHEREu.[Age]IN'Demo.Program+<>c__DisplayClass0'[arr]
问题
问题在于arr和u.Age一样都是MemberExpression,而MemberExpression的解析之前是这样写的
classMemberExpressionParser:
ExpressionParser
{
publicoverridevoidWhere(MemberExpressionexpr,ParserArgsargs)
{
Parser.Where(expr.Expression,args);
args.Builder.Append('[');
args.Builder.Append(expr.Member.Name);
args.Builder.Append(']');
}
......
}
显然MemberExpression有两种,一种是`虚拟的`,是不存在值的,比如u.Age,
还有一种是真实的比如上面例子中的arr,他是有真实值的
所以这个地方要改一改
代码
这块地方比较难,需要理解一下
classMemberExpressionParser:
ExpressionParser
{
publicoverridevoidWhere(MemberExpressionexpr,ParserArgsargs)
{
if(expr.ExpressionisParameterExpression)
{
Parser.Where(expr.Expression,args);
args.Builder.Append('[');
args.Builder.Append(expr.Member.Name);
args.Builder.Append(']');
}
else
{
objectval=GetValue(expr);
args.Builder.Append('');
IEnumeratorarray=valasIEnumerator;
if(array!
=null)
{
AppendArray(args,array);
}
elseif(valisIEnumerable)
{
AppendArray(args,((IEnumerable)val).GetEnumerator());
}
else
{
AppendObject(args,val);
}
}
}
///
///
privatestaticobjectGetValue(MemberExpressionexpr)
{
objectval;
varfield=expr.MemberasFieldInfo;
if(field!
=null)
{
val=field.GetValue(((ConstantExpression)expr.Expression).Value);
}
else
{
val=((PropertyInfo)expr.Member).GetValue(((ConstantExpression)expr.Expression).Value,null);
}
returnval;
}
///
///
privatestaticvoidAppendArray(ParserArgsargs,IEnumeratorarray)
{
if(array.MoveNext())
{
args.Builder.Append('(');
AppendObject(args,array.Current);
while(array.MoveNext())
{
args.Builder.Append(',');
AppendObject(args,array.Current);
}
args.Builder.Append(')');
}
else
{
args.Builder.Append("NULL");
}
}
///
///
publicstaticvoidAppendObject(ParserArgsargs,objectval)
{
if(val==null||val==DBNull.Value)
{
args.Builder.Append("NULL");
}
elseif(valisbool)
{
args.Builder.Append(val.GetHashCode());
}
else
{
varcode=(int)Type.GetTypeCode(val.GetType());
if(code>=5&&code<=15)//如果expr.Value是数字类型
{
args.Builder.Append(val);
}
else
{
args.Builder.Append('\'');
args.Builder.Append(val);
args.Builder.Append('\'');
}
}
}
......
}
结果
int[]arr={13,15,17,19,21};
db.Where
//打印
//SELECT*FROM[User]uWHEREu.[Age]IN(13,15,17,19,21)
问题四
如果需要使用参数化传递参数,又需要怎样修改源码呢?
分析
其实这个问题是最简单的一个问题,如果已经理解这个`框架`的工作原理可以轻松解决这个问题
代码
1.修改ParserArgs,使其中包含一个SqlParamete的集合,并且为了方便操作,将AppendObject的方法也移入ParserArgs,变为AddParameter
使用参数化传递还有一个好处可以不用判断参数类型,来确定是否添加单引号(')
publicclassParserArgs
{
publicParserArgs()
{
Builder=newStringBuilder();
SqlParameters=newList
}
publicList
publicStringBuilderBuilder{get;privateset;}
///
///
publicvoidAddParameter(objectobj)
{
if
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 干货 表达式 解析 框架
![提示](https://static.bdocx.com/images/bang_tan.gif)