0%

抛弃EF,20分构建一个属于自己的ORM框架 - Poiuyt_cyc - 博客园

Excerpt

相信EF大家都不陌生了,因为数据库表跟程序实体是一一对应的原因,我们能够通过lambda这种函数式的编程方式进行操作数据库,感觉非常清晰明了。与我们直接写SQL相比,lambda是强类型,拥有更好的扩展性,伸缩性,而且编程更加的方便,快捷。。下面我们就基于Expression和lambda来与大家构


相信EF大家都不陌生了,因为数据库表跟程序实体是一一对应的原因,我们能够通过lambda这种函数式的编程方式进行操作数据库,感觉非常清晰明了。与我们直接写SQL相比,lambda是强类型,拥有更好的扩展性,伸缩性,而且编程更加的方便,快捷。。下面我们就基于Expression和lambda来与大家构建一个属于自己的ORM框架。

思路的话很简单,就是将lambda转换成我们对应的数据库所需的查询条件,然后执行查询,再将结果以反射的方式封装成List返回出去。

Expression

大家使用EF的时候多多少少会留意到有Expression这个东西。特别是查询时会看到要你传入Expression<Func<T,bool>>这样类型的参数,它又和Func<T,bool>有什么比同呢?

Expression<Func<T,bool>>是表达式树,我们可以通过它来分析我们的委托中的函数。当调用Compile方法后就会变成委托,才能执行。

Func<T,bool>只是一个普通的委托。

例如我们现在有个实体类Staff

复制代码

1
2
3
4
5
6
7
8
<span>public</span> <span>class</span><span> Staff
{
</span><span>public</span> <span>string</span> Name { <span>get</span>; <span>set</span><span>; }
</span><span>public</span> <span>int</span> Age { <span>get</span>; <span>set</span><span>; }
</span><span>public</span> <span>string</span> Code { <span>get</span>; <span>set</span><span>; }
</span><span>public</span> DateTime? Birthday { <span>get</span>; <span>set</span><span>; }
</span><span>public</span> <span>bool</span> Deletion { <span>get</span>; <span>set</span><span>; }
}</span>

复制代码

我们还有一个这样的方法

复制代码

1
2
3
4
5
6
7
8
9
10
11
12
13
  <span>class</span><span> Program
{
</span><span>static</span> <span>void</span> Main(<span>string</span><span>[] args)
{
FindAs</span>&lt;Staff&gt;(x =&gt; x.Code == <span>"</span><span>张三</span><span>"</span> &amp;&amp; x.Name.Contains(<span>"</span><span>张</span><span>"</span><span>));
}

</span><span>public</span> <span>static</span> List&lt;T&gt; FindAs&lt;T&gt;(Expression&lt;Func&lt;T, <span>bool</span>&gt;&gt;<span> func)
{
</span><span>//</span><span>将func转换成对应数据库的查询条件,然后执行查询</span>
<span>return</span> <span>null</span>;<span>//</span><span>将结果返回</span>
<span> }
}</span>

复制代码

我们希望通过 FindAs(x => x.Age <50 && x.Name.Contains(“张”)); 就能查询出Staff表中Age<50并且Name包含有“张”字的人的信息。而生成的sql语句应该是select * from staff where Age<50 and Name like ‘%张%’。现在我们就来分析下这个func

从上面的图我们可以看到当前的Expression是一个lambda表达式,我们点开它的body看看。

我们可以看到body里分为左边和右边,还有NodeType。和我们的lambda对比下看看’x => x.Code ==”张三” && x.Name.Contains(“张”)’是不是找到点灵感了?我们再继续把左边和右边拆开看看。

可以看到我们需要的信息都有了,看来转换成SQL已经不是什么难事了,动手开搞了。

复制代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
 <span>class</span><span> Program
{
</span><span>static</span> <span>void</span> Main(<span>string</span><span>[] args)
{
FindAs</span>&lt;Staff&gt;(x =&gt; x.Code == <span>"</span><span>张三</span><span>"</span> &amp;&amp; x.Name.Contains(<span>"</span><span>张</span><span>"</span><span>));
FindAs</span>&lt;Staff&gt;(x =&gt; x.Age &lt;= <span>12</span> &amp;&amp; x.Name.Contains(<span>"</span><span>张</span><span>"</span><span>));
Console.ReadKey();
}

</span><span>public</span> <span>static</span> List&lt;T&gt; FindAs&lt;T&gt;(Expression&lt;Func&lt;T, <span>bool</span>&gt;&gt;<span> func)
{
BinaryExpression Binary </span>= func.Body <span>as</span><span> BinaryExpression;
</span><span>string</span> left =<span> ResovleFunc(Binary.Left);
</span><span>string</span> right =<span> ResovleLinqToObject(Binary.Right);
</span><span>string</span> oper =<span> GetOperator(Binary.NodeType);
</span><span>string</span> sql = <span>string</span>.Format(<span>"</span><span>select * from {0} where {1}</span><span>"</span>, <span>typeof</span>(T).Name, left + oper +<span> right);
Console.WriteLine(sql);
</span><span>return</span> <span>null</span>;<span>//</span><span>将结果返回</span>
<span> }

</span><span>//</span><span>解析一般的条件,例如x=&gt;x.name==xxxx x.age==xxx</span>
<span>public</span> <span>static</span> <span>string</span><span> ResovleFunc(Expression express)
{
</span><span>var</span> inner = express <span>as</span><span> BinaryExpression;
</span><span>string</span> Name = (inner.Left <span>as</span><span> MemberExpression).Member.Name;
</span><span>object</span> Value = (inner.Right <span>as</span><span> ConstantExpression).Value;
</span><span>var</span> Operator =<span> GetOperator(inner.NodeType);
</span><span>string</span> Result = <span>string</span>.Format(<span>"</span><span>({0} {1} '{2}')</span><span>"</span><span>, Name, Operator, Value);
</span><span>return</span><span> Result;
}

</span><span>//</span><span>解析linq to object这类扩展方法</span>
<span>public</span> <span>static</span> <span>string</span><span> ResovleLinqToObject(Expression expression)
{
</span><span>var</span> MethodCall = expression <span>as</span><span> MethodCallExpression;
</span><span>var</span> MethodName =<span> MethodCall.Method.Name;
</span><span>if</span> (MethodName == <span>"</span><span>Contains</span><span>"</span><span>)
{
</span><span>object</span> Temp_Vale = (MethodCall.Arguments[<span>0</span>] <span>as</span><span> ConstantExpression).Value;
</span><span>string</span> Value = <span>string</span>.Format(<span>"</span><span>%{0}%</span><span>"</span><span>, Temp_Vale);
</span><span>string</span> Name = (MethodCall.Object <span>as</span><span> MemberExpression).Member.Name;
</span><span>string</span> Result = <span>string</span>.Format(<span>"</span><span>{0} like '{1}'</span><span>"</span><span>, Name, Value);
</span><span>return</span><span> Result;
}
</span><span>return</span> <span>null</span><span>;
}

</span><span>public</span> <span>static</span> <span>string</span><span> GetOperator(ExpressionType expressiontype)
{
</span><span>switch</span><span> (expressiontype)
{
</span><span>case</span><span> ExpressionType.And:
</span><span>return</span> <span>"</span><span>and</span><span>"</span><span>;
</span><span>case</span><span> ExpressionType.AndAlso:
</span><span>return</span> <span>"</span><span>and</span><span>"</span><span>;
</span><span>case</span><span> ExpressionType.Or:
</span><span>return</span> <span>"</span><span>or</span><span>"</span><span>;
</span><span>case</span><span> ExpressionType.OrElse:
</span><span>return</span> <span>"</span><span>or</span><span>"</span><span>;
</span><span>case</span><span> ExpressionType.Equal:
</span><span>return</span> <span>"</span><span>=</span><span>"</span><span>;
</span><span>case</span><span> ExpressionType.NotEqual:
</span><span>return</span> <span>"</span><span>&lt;&gt;</span><span>"</span><span>;
</span><span>case</span><span> ExpressionType.LessThan:
</span><span>return</span> <span>"</span><span>&lt;</span><span>"</span><span>;
</span><span>case</span><span> ExpressionType.LessThanOrEqual:
</span><span>return</span> <span>"</span><span>&lt;=</span><span>"</span><span>;
</span><span>case</span><span> ExpressionType.GreaterThan:
</span><span>return</span> <span>"</span><span>&gt;</span><span>"</span><span>;
</span><span>case</span><span> ExpressionType.GreaterThanOrEqual:
</span><span>return</span> <span>"</span><span>&gt;=</span><span>"</span><span>;
</span><span>default</span><span>:
</span><span>throw</span> <span>new</span> Exception(<span>string</span>.Format(<span>"</span><span>不支持{0}此种运算符查找!</span><span>"</span> +<span> expressiontype));
}
}

}</span>

复制代码

已经初步的达到了我们的目的了,但是我们的查询条件不可能固定是2个,有可能是N个,这时左边和右边又要继续再分下去,直到无法再分(想到递归了吧?)。而且我们还需要将查询条件参数化。而且我们的条件删除时也会用到。所以我们应该把它独立出来。传入一个lambda,生成sql where部分的语句,生成sqlparameter[]。这才是关键。。于是我们来构建一个解析Expresstion的类。。下面我就直接给出我自己写的实现代码了。。

复制代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
 <span>public</span> <span>class</span><span> ResolveExpress
{
</span><span>public</span> Dictionary&lt;<span>string</span>, <span>object</span>&gt;<span> Argument;
</span><span>public</span> <span>string</span><span> SqlWhere;
</span><span>public</span><span> SqlParameter[] Paras;

</span><span>///</span> <span>&lt;summary&gt;</span>
<span>///</span><span> 解析lamdba,生成Sql查询条件
</span><span>///</span> <span>&lt;/summary&gt;</span>
<span>///</span> <span>&lt;param name="expression"&gt;&lt;/param&gt;</span>
<span>///</span> <span>&lt;returns&gt;&lt;/returns&gt;</span>
<span>public</span> <span>void</span><span> ResolveExpression(Expression expression)
{
</span><span>this</span>.Argument = <span>new</span> Dictionary&lt;<span>string</span>, <span>object</span>&gt;<span>();
</span><span>this</span>.SqlWhere =<span> Resolve(expression);
</span><span>this</span>.Paras = Argument.Select(x =&gt; <span>new</span><span> SqlParameter(x.Key, x.Value)).ToArray();
}

</span><span>private</span> <span>string</span><span> Resolve(Expression expression)
{
</span><span>if</span> (expression <span>is</span><span> LambdaExpression)
{
LambdaExpression lambda </span>= expression <span>as</span><span> LambdaExpression;
expression </span>=<span> lambda.Body;
</span><span>return</span><span> Resolve(expression);
}
</span><span>if</span> (expression <span>is</span><span> BinaryExpression)
{
BinaryExpression binary </span>= expression <span>as</span><span> BinaryExpression;
</span><span>if</span> (binary.Left <span>is</span> MemberExpression &amp;&amp; binary.Right <span>is</span> ConstantExpression)<span>//</span><span>解析x=&gt;x.Name=="123" x.Age==123这类</span>
<span>return</span><span> ResolveFunc(binary.Left, binary.Right, binary.NodeType);
</span><span>if</span> (binary.Left <span>is</span> MethodCallExpression &amp;&amp; binary.Right <span>is</span> ConstantExpression)<span>//</span><span>解析x=&gt;x.Name.Contains("xxx")==false这类的</span>
<span> {
</span><span>object</span> value = (binary.Right <span>as</span><span> ConstantExpression).Value;
</span><span>return</span><span> ResolveLinqToObject(binary.Left, value, binary.NodeType);
}
</span><span>if</span> (binary.Left <span>is</span> MemberExpression &amp;&amp; binary.Right <span>is</span> MemberExpression)<span>//</span><span>解析x=&gt;x.Date==DateTime.Now这种</span>
<span> {
LambdaExpression lambda </span>=<span> Expression.Lambda(binary.Right);
Delegate fn </span>=<span> lambda.Compile();
ConstantExpression value </span>= Expression.Constant(fn.DynamicInvoke(<span>null</span><span>), binary.Right.Type);
</span><span>return</span><span> ResolveFunc(binary.Left, value, binary.NodeType);
}
}
</span><span>if</span> (expression <span>is</span><span> UnaryExpression)
{
UnaryExpression unary </span>= expression <span>as</span><span> UnaryExpression;
</span><span>if</span> (unary.Operand <span>is</span> MethodCallExpression)<span>//</span><span>解析!x=&gt;x.Name.Contains("xxx")或!array.Contains(x.Name)这类</span>
<span>return</span> ResolveLinqToObject(unary.Operand, <span>false</span><span>);
</span><span>if</span> (unary.Operand <span>is</span> MemberExpression &amp;&amp; unary.NodeType == ExpressionType.Not)<span>//</span><span>解析x=&gt;!x.isDeletion这样的 </span>
<span> {
ConstantExpression constant </span>= Expression.Constant(<span>false</span><span>);
</span><span>return</span><span> ResolveFunc(unary.Operand, constant, ExpressionType.Equal);
}
}
</span><span>if</span> (expression <span>is</span> MemberExpression &amp;&amp; expression.NodeType == ExpressionType.MemberAccess)<span>//</span><span>解析x=&gt;x.isDeletion这样的 </span>
<span> {
MemberExpression member </span>= expression <span>as</span><span> MemberExpression;
ConstantExpression constant </span>= Expression.Constant(<span>true</span><span>);
</span><span>return</span><span> ResolveFunc(member, constant, ExpressionType.Equal);
}
</span><span>if</span> (expression <span>is</span> MethodCallExpression)<span>//</span><span>x=&gt;x.Name.Contains("xxx")或array.Contains(x.Name)这类</span>
<span> {
MethodCallExpression methodcall </span>= expression <span>as</span><span> MethodCallExpression;
</span><span>return</span> ResolveLinqToObject(methodcall, <span>true</span><span>);
}
</span><span>var</span> body = expression <span>as</span><span> BinaryExpression;
</span><span>if</span> (body == <span>null</span><span>)
</span><span>throw</span> <span>new</span> Exception(<span>"</span><span>无法解析</span><span>"</span> +<span> expression);
</span><span>var</span> Operator =<span> GetOperator(body.NodeType);
</span><span>var</span> Left =<span> Resolve(body.Left);
</span><span>var</span> Right =<span> Resolve(body.Right);
</span><span>string</span> Result = <span>string</span>.Format(<span>"</span><span>({0} {1} {2})</span><span>"</span><span>, Left, Operator, Right);
</span><span>return</span><span> Result;
}

</span><span>///</span> <span>&lt;summary&gt;</span>
<span>///</span><span> 根据条件生成对应的sql查询操作符
</span><span>///</span> <span>&lt;/summary&gt;</span>
<span>///</span> <span>&lt;param name="expressiontype"&gt;&lt;/param&gt;</span>
<span>///</span> <span>&lt;returns&gt;&lt;/returns&gt;</span>
<span>private</span> <span>string</span><span> GetOperator(ExpressionType expressiontype)
{
</span><span>switch</span><span> (expressiontype)
{
</span><span>case</span><span> ExpressionType.And:
</span><span>return</span> <span>"</span><span>and</span><span>"</span><span>;
</span><span>case</span><span> ExpressionType.AndAlso:
</span><span>return</span> <span>"</span><span>and</span><span>"</span><span>;
</span><span>case</span><span> ExpressionType.Or:
</span><span>return</span> <span>"</span><span>or</span><span>"</span><span>;
</span><span>case</span><span> ExpressionType.OrElse:
</span><span>return</span> <span>"</span><span>or</span><span>"</span><span>;
</span><span>case</span><span> ExpressionType.Equal:
</span><span>return</span> <span>"</span><span>=</span><span>"</span><span>;
</span><span>case</span><span> ExpressionType.NotEqual:
</span><span>return</span> <span>"</span><span>&lt;&gt;</span><span>"</span><span>;
</span><span>case</span><span> ExpressionType.LessThan:
</span><span>return</span> <span>"</span><span>&lt;</span><span>"</span><span>;
</span><span>case</span><span> ExpressionType.LessThanOrEqual:
</span><span>return</span> <span>"</span><span>&lt;=</span><span>"</span><span>;
</span><span>case</span><span> ExpressionType.GreaterThan:
</span><span>return</span> <span>"</span><span>&gt;</span><span>"</span><span>;
</span><span>case</span><span> ExpressionType.GreaterThanOrEqual:
</span><span>return</span> <span>"</span><span>&gt;=</span><span>"</span><span>;
</span><span>default</span><span>:
</span><span>throw</span> <span>new</span> Exception(<span>string</span>.Format(<span>"</span><span>不支持{0}此种运算符查找!</span><span>"</span> +<span> expressiontype));
}
}


</span><span>private</span> <span>string</span><span> ResolveFunc(Expression left, Expression right, ExpressionType expressiontype)
{
</span><span>var</span> Name = (left <span>as</span><span> MemberExpression).Member.Name;
</span><span>var</span> Value = (right <span>as</span><span> ConstantExpression).Value;
</span><span>var</span> Operator =<span> GetOperator(expressiontype);
</span><span>string</span> CompName =<span> SetArgument(Name, Value.ToString());
</span><span>string</span> Result = <span>string</span>.Format(<span>"</span><span>({0} {1} {2})</span><span>"</span><span>, Name, Operator, CompName);
</span><span>return</span><span> Result;
}

</span><span>private</span> <span>string</span> ResolveLinqToObject(Expression expression, <span>object</span> value, ExpressionType? expressiontype = <span>null</span><span>)
{
</span><span>var</span> MethodCall = expression <span>as</span><span> MethodCallExpression;
</span><span>var</span> MethodName =<span> MethodCall.Method.Name;
</span><span>switch</span> (MethodName)<span>//</span><span>这里其实还可以改成反射调用,不用写switch</span>
<span> {
</span><span>case</span> <span>"</span><span>Contains</span><span>"</span><span>:
</span><span>if</span> (MethodCall.Object != <span>null</span><span>)
</span><span>return</span><span> Like(MethodCall);
</span><span>return</span><span> In(MethodCall, value);
</span><span>case</span> <span>"</span><span>Count</span><span>"</span><span>:
</span><span>return</span><span> Len(MethodCall, value, expressiontype.Value);
</span><span>case</span> <span>"</span><span>LongCount</span><span>"</span><span>:
</span><span>return</span><span> Len(MethodCall, value, expressiontype.Value);
</span><span>default</span><span>:
</span><span>throw</span> <span>new</span> Exception(<span>string</span>.Format(<span>"</span><span>不支持{0}方法的查找!</span><span>"</span><span>, MethodName));
}
}

</span><span>private</span> <span>string</span> SetArgument(<span>string</span> name, <span>string</span><span> value)
{
name </span>= <span>"</span><span>@</span><span>"</span> +<span> name;
</span><span>string</span> temp =<span> name;
</span><span>while</span><span> (Argument.ContainsKey(temp))
{
</span><span>int</span> code =<span> Guid.NewGuid().GetHashCode();
</span><span>if</span> (code &lt; <span>0</span><span>)
code </span>*= -<span>1</span><span>;
temp </span>= name +<span> code;
}
Argument[temp] </span>=<span> value;
</span><span>return</span><span> temp;
}

</span><span>private</span> <span>string</span> In(MethodCallExpression expression, <span>object</span><span> isTrue)
{
</span><span>var</span> Argument1 = (expression.Arguments[<span>0</span>] <span>as</span> MemberExpression).Expression <span>as</span><span> ConstantExpression;
</span><span>var</span> Argument2 = expression.Arguments[<span>1</span>] <span>as</span><span> MemberExpression;
</span><span>var</span> Field_Array =<span> Argument1.Value.GetType().GetFields().First();
</span><span>object</span>[] Array = Field_Array.GetValue(Argument1.Value) <span>as</span> <span>object</span><span>[];
List</span>&lt;<span>string</span>&gt; SetInPara = <span>new</span> List&lt;<span>string</span>&gt;<span>();
</span><span>for</span> (<span>int</span> i = <span>0</span>; i &lt; Array.Length; i++<span>)
{
</span><span>string</span> Name_para = <span>"</span><span>InParameter</span><span>"</span> +<span> i;
</span><span>string</span> Value =<span> Array[i].ToString();
</span><span>string</span> Key =<span> SetArgument(Name_para, Value);
SetInPara.Add(Key);
}
</span><span>string</span> Name =<span> Argument2.Member.Name;
</span><span>string</span> Operator = Convert.ToBoolean(isTrue) ? <span>"</span><span>in</span><span>"</span> : <span>"</span><span> not in</span><span>"</span><span>;
</span><span>string</span> CompName = <span>string</span>.Join(<span>"</span><span>,</span><span>"</span><span>, SetInPara);
</span><span>string</span> Result = <span>string</span>.Format(<span>"</span><span>{0} {1} ({2})</span><span>"</span><span>, Name, Operator, CompName);
</span><span>return</span><span> Result;
}

</span><span>private</span> <span>string</span><span> Like(MethodCallExpression expression)
{
</span><span>object</span> Temp_Vale = (expression.Arguments[<span>0</span>] <span>as</span><span> ConstantExpression).Value;
</span><span>string</span> Value = <span>string</span>.Format(<span>"</span><span>%{0}%</span><span>"</span><span>, Temp_Vale);
</span><span>string</span> Name = (expression.Object <span>as</span><span> MemberExpression).Member.Name;
</span><span>string</span> CompName =<span> SetArgument(Name, Value);
</span><span>string</span> Result = <span>string</span>.Format(<span>"</span><span>{0} like {1}</span><span>"</span><span>, Name, CompName);
</span><span>return</span><span> Result;
}

</span><span>private</span> <span>string</span> Len(MethodCallExpression expression, <span>object</span><span> value, ExpressionType expressiontype)
{
</span><span>object</span> Name = (expression.Arguments[<span>0</span>] <span>as</span><span> MemberExpression).Member.Name;
</span><span>string</span> Operator =<span> GetOperator(expressiontype);
</span><span>string</span> CompName =<span> SetArgument(Name.ToString(), value.ToString());
</span><span>string</span> Result = <span>string</span>.Format(<span>"</span><span>len({0}){1}{2}</span><span>"</span><span>, Name, Operator, CompName);
</span><span>return</span><span> Result;
}

}</span>

复制代码

复制代码

1
2
3
4
5
6
7
8
9
10
11
12
13
<span>static</span> <span>void</span> Main(<span>string</span><span>[] args)
{
</span><span>string</span>[] Names = { <span>"</span><span>Andy</span><span>"</span>, <span>"</span><span>Amy</span><span>"</span>, <span>"</span><span>Mike</span><span>"</span><span> };
Expression</span>&lt;Func&lt;Staff, <span>bool</span>&gt;&gt; func = x =&gt; (!Names.Contains(x.Name) &amp;&amp; (x.Name == <span>"</span><span>A</span><span>"</span> || x.Name.Count() &gt; <span>5</span><span>));
ResolveExpress resolve </span>= <span>new</span><span> ResolveExpress();
resolve.ResolveExpression(func);
Console.WriteLine(resolve.SqlWhere);
</span><span>foreach</span> (<span>var</span> item <span>in</span><span> resolve.Paras)
{
Console.WriteLine(item.ParameterName </span>+ <span>"</span><span>:</span><span>"</span> +<span> item.Value);
}
Console.ReadKey();
}</span>

复制代码

结果:

这里有几个重要的东西要给大家讲下

string[] Names={“Andy”,”Amy”,”Mike”};

1.)x => Names.Contains(x.Name);

2.)x => Names.Contains(x.Name)==false;

3.)x => !Names.Contains(x.Name);

这3种在Expression中的表现都不一样

1的话会看成是一个静态方法(MethodCallExpression)

2的话会看成是一个2元运算(BinaryExpression)

3的话会看成是一个1元运算(UnaryExpression)

所以我们都要支持,处理都有所不同。

还有

x=>x.Birthday<DateTime.Now;

string name=”123”;

x=>x.Name==name;

x=>x.Name==”123”

的处理也不一样。大家可以在例子中细细的看看。

这样的构造使得我们切换数据库变得非常简单。因为我们程序中的查询都是基于lambda。换了数据库只要添加一个对应的lamdba转数据库查询条件的实现就可以了。写得够多了。至于数据层怎么封装,到了这一步它已经变得没什么难度了。希望大家能从文章中有所启发和帮助

下篇文章将结合解析Expression和IQueryable来实现延迟加载

补充点东西

IEnumerable和IQueryable有什么不同?

为什么EF查询后返回的是IQueryable而不是IEnumerable。我们对着IQueryableF12去看看。

啥都没,就继承了几个接口。鼠标移到IQueryable上。F12

IQueryable中有3个属性。

Type是类型。

Expresstion是表达式。

那IQueryProvider是什么?

再看看IQueryProvider接口的定义。

CreateQuery是创建查询条件

Execute是执行查询(通常在GetEnumerator()中调用)

当我们IQueryable.Where(x=>x.xxx==”123”)时。其实Where方法内部应该是调用了IQueryable接口中的IQueryProvider属性的CreateQuery(Expresstion expresstion)方法,然后将方法的返回值又返回出来。

而参数(Expresstion )呢?则是IQueryable.Where(x=>x.xxx==”123”)2部分的Expresstion相并。所以IQueryable只是创建条件。所以51楼的朋友说得非常对。

那什么时候执行呢?因为我们的IQueryable继承了IEnumabler,所以我们必须实现GetEnumerator()。我们ToList或foreach时,其实就会调用GetEnumerator()。这时我们就调用Execute进行解析Expresstion,从而得到我们想要的结果。

总结就是IQueryable只是创建条件,当我们调用a.Where(x=>xxx)时,其实是将a与后面的条件相并,生成一个新的IQueryable。当我们foreach时就会调用GetEnumerator()。这时我们一般会调用IQueryProvider里的Execute去解析Expresstion并查询出我们想要的结果

我也知道这篇文章介绍的和我们所说的“ORM”相差很远,但是所谓的ORM最复杂的莫非查询部分了,而依照我这思路走下去,我觉得是可以自己完成一个的。。

我刚开始写博客第二天,没想到这文章反响这么大。我承认有点重复造轮子,也非常不成熟,但我还是想通过自己的思考去构造属于自己的东西。

不知道大家有没看过头文字D,里头有个组织叫东堂垫,他们里面的人是拆掉ABS的。因为他们会长说,你要先学会不使用ABS进行刹车才知道ABS的真谛