干货表达式树解析框架3Word格式.docx
《干货表达式树解析框架3Word格式.docx》由会员分享,可在线阅读,更多相关《干货表达式树解析框架3Word格式.docx(20页珍藏版)》请在冰豆网上搜索。
![干货表达式树解析框架3Word格式.docx](https://file1.bdocx.com/fileroot1/2022-11/30/721e2719-5b9f-4a03-b487-b15d8129c2f7/721e2719-5b9f-4a03-b487-b15d8129c2f71.gif)
NULL"
就算是NULL了
但是这里有个问题,就是原来的操作是先加入符号,再加入Right的,所以这里也要改,改为先放入Right再插入符号
代码如下
classBinaryExpressionParser:
ExpressionParser<
BinaryExpression>
{
publicoverridevoidWhere(BinaryExpressionexpr,ParserArgsargs)
if(ExistsBracket(expr.Left))
args.Builder.Append('
'
);
('
Parser.Where(expr.Left,args);
)'
}
else
varindex=args.Builder.Length;
if(ExistsBracket(expr.Right))
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]=='
)
Sign(expr.NodeType,index,args,true);
Sign(expr.NodeType,index,args);
///<
summary>
判断是否需要添加括号
/summary>
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)
IS"
="
caseExpressionType.GreaterThan:
>
"
caseExpressionType.GreaterThanOrEqual:
="
caseExpressionType.NotEqual:
ISNOT"
<
caseExpressionType.Or:
caseExpressionType.OrElse:
OR"
caseExpressionType.LessThan:
caseExpressionType.LessThanOrEqual:
default:
thrownewNotImplementedException("
无法解释节点类型"
+type);
......
结果
//打印SELECT*FROM[User]uWHEREu.[Name]ISNOTNULL
问题二
u.Name.StartsWith("
bl"
));
//u.Namelike'
bl%'
这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<
MethodInfo,Action<
MethodCallExpression,ParserArgs>
_Methods=MethodDitcInit();
privatestaticDictionary<
MethodDitcInit()
Dictionary<
dict=newDictionary<
();
vartype=typeof(string);
foreach(varmetintype.GetMethods())
switch(met.Name)
case"
StartsWith"
:
dict.Add(met,String_StartsWith);
Contains"
dict.Add(met,String_Contains);
EndsWith"
dict.Add(met,String_EndsWith);
returndict;
调用where
publicoverridevoidWhere(MethodCallExpressionexpr,ParserArgsargs)
Action<
act;
if(_Methods.TryGetValue(expr.Method,outact))
act(expr,args);
return;
无法解释方法"
+expr.Method);
现在分别完成3个String_的函数就可以了
publicstaticvoidString_StartsWith(MethodCallExpressionexpr,ParserArgsargs)
Parser.Where(expr.Object,args);
args.Builder.Append("
LIKE"
Parser.Where(expr.Arguments[0],args);
+'
%'
LIKE'
+"
u.Name.Contains("
u.Name.EndsWith("
/*打印
SELECT*FROM[User]uWHEREu.[Name]LIKE'
bl'
*/
问题三
int[]arr={13,15,17,19,21};
arr.Contains(u.Age));
//u.Agein(13,15,17,19,21)
这个问题和刚才那个问题有很多相似之处,所以首先需要在MethodCallExpressionParser类中实现一个对应Enumerable.Contains的解析函数
但是这个方法有一个比较特殊的地方就是他是泛型方法,所以在从键值对中获取处理函数的时候,需要把他转为`泛型方法定义`(MethodInfo.GetGenericMethodDefinition)的才可以
varkey=expr.Method;
if(key.IsGenericMethod)
key=key.GetGenericMethodDefinition();
}
if(_Methods.TryGetValue(key,outact))
对应的处理函数
publicstaticvoidEnumerable_Contains(MethodCallExpressionexpr,ParserArgsargs)
Parser.Where(expr.Arguments[1],args);
IN"
看上去似乎已经完成了,但是结果是....
//打印
//SELECT*FROM[User]uWHEREu.[Age]IN'
Demo.Program+<
c__DisplayClass0'
[arr]
问题
问题在于arr和u.Age一样都是MemberExpression,而MemberExpression的解析之前是这样写的
classMemberExpressionParser:
ExpressionParser<
MemberExpression>
publicoverridevoidWhere(MemberExpressionexpr,ParserArgsargs)
Parser.Where(expr.Expression,args);
['
args.Builder.Append(expr.Member.Name);
]'
显然MemberExpression有两种,一种是`虚拟的`,是不存在值的,比如u.Age,
还有一种是真实的比如上面例子中的arr,他是有真实值的
所以这个地方要改一改
代码
这块地方比较难,需要理解一下
classMemberExpressionParser:
if(expr.ExpressionisParameterExpression)
objectval=GetValue(expr);
IEnumeratorarray=valasIEnumerator;
if(array!
=null)
AppendArray(args,array);
elseif(valisIEnumerable)
AppendArray(args,((IEnumerable)val).GetEnumerator());
AppendObject(args,val);
获取成员表达式中的实际值
privatestaticobjectGetValue(MemberExpressionexpr)
objectval;
varfield=expr.MemberasFieldInfo;
if(field!
val=field.GetValue(((ConstantExpression)expr.Expression).Value);
val=((PropertyInfo)expr.Member).GetValue(((ConstantExpression)expr.Expression).Value,null);
returnval;
追加可遍历对象(数组或集合或简单迭代器)
privatestaticvoidAppendArray(ParserArgsargs,IEnumeratorarray)
if(array.MoveNext())
AppendObject(args,array.Current);
while(array.MoveNext())
'
NULL"
追加一般对象
publicstaticvoidAppendObject(ParserArgsargs,objectval)
if(val==null||val==DBNull.Value)
elseif(valisbool)
args.Builder.Append(val.GetHashCode());
varcode=(int)Type.GetTypeCode(val.GetType());
if(code>
=5&
code<
=15)//如果expr.Value是数字类型
args.Builder.Append(val);
\'
'
//SELECT*FROM[User]uWHEREu.[Age]IN(13,15,17,19,21)
问题四
如果需要使用参数化传递参数,又需要怎样修改源码呢?
其实这个问题是最简单的一个问题,如果已经理解这个`框架`的工作原理可以轻松解决这个问题
1.修改ParserArgs,使其中包含一个SqlParamete的集合,并且为了方便操作,将AppendObject的方法也移入ParserArgs,变为AddParameter
使用参数化传递还有一个好处可以不用判断参数类型,来确定是否添加单引号('
publicclassParserArgs
publicParserArgs()
Builder=newStringBuilder();
SqlParameters=newList<
SqlParameter>
publicList<
SqlParameters{get;
set;
publicStringBuilderBuilder{get;
privateset;
追加参数
publicvoidAddParameter(objectobj)
if