使用EF构建企业级应用(二)

2012-04-07 16:49 by 谢中涞, 462 visits, 收藏编辑

 动态排序扩展

在上一节(使用EF构建企业级应用(一) ) 中,我们实现了数据库基本操作的CURD的定义,如果你直接复制这个代码到VS中编译,奇怪的问题就出现了,可能会出好几个错误,可能错误发生在类似这样的代码上”IQueryable<TEntity> query = Get(expression).OrderBy(orderPropertyName, isAscending);”,大致的错误可能是image,这是为啥呢? 该不会是楼主忽悠吧,这个自然不会,且听如下分解.

我们常使用的排序可能是如下样子:var temp=lst.OrderBy(t=>t.Code).ToList();因为后端我们并不清楚在数据库查询的时候并不清楚是对什么字段排序,那么我们如何动态来构建这个类似于OrderBy中的(t=>t.Code)表达式呢?

在定义中”public static IOrderedQueryable<TSource> OrderBy<TSource, TKey>(this IQueryable<TSource> source, Expression<Func<TSource, TKey>> keySelector) ”,

我们知道这个OrderBy方法其实是需要接受一个Expression<Func<TSource, TKey>> 表达式树的参数.问题转化为如何动态构建这个表达式树,在学习一段时间Expression相关知识+不断google+多次调试后,于是便有了下面这个方法扩展.
       /// <summary>
        /// 动态排序
         /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="source"></param>
        /// <param name="propertyName">按T类型中排序属性名</param>
        /// <param name="isAscending">是否是升序排序</param>
        /// <returns></returns>
        public static IOrderedQueryable<T> OrderBy<T>(this IQueryable<T> source, string propertyName, bool isAscending) where T : class
        {            
                var type = typeof(T);
                Expression resultExp = null;
                var property = type.GetProperty(propertyName);
                if (property == null)
                    throw new ArgumentException("propertyName", "不存在");

                var param = Expression.Parameter(type, "p");
                Expression propertyAccessExpression = Expression.MakeMemberAccess(param, property);
                var orderByExpression = Expression.Lambda(propertyAccessExpression, param);
                var methodName = isAscending ? "OrderBy" : "OrderByDescending";
                resultExp = Expression.Call(typeof(Queryable), methodName, new Type[] { type, property.PropertyType },
                                               source.Expression, Expression.Quote(orderByExpression));
                return (IOrderedQueryable<T>)source.Provider.CreateQuery<T>(resultExp);
            
        }

上面方法终于解决动态排序的问题,直到有一天,业务上在查询产品信息的时候,需要按照产品大类名称排序,我们很自然的写成了var lst=lst.OrderBy(“ProductCategory.Name”,true)这样的调用,结果却出现了类似于image这样的错误,大致意思就是当前属性在指定的类型中不存在,很显然这样的写法只支持按实体简单的属性排序,没有办法做关联排序,为让大家更明白一点,把产品和产品大类定义描述一下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
  /// <summary>
  /// 产品信息
  /// </summary>
  public class Product
  {
  public Guid Id{get;set;}
  public string Name{get;set;}
  //产品大类
  public ProductCategory ProductCategory{get;set;}
 
 }
 public class ProductCategory
{
  public Guid Id{get;set;}
  public string Name{get;set;}
}

想必大家明白了问题的所在,我们是需要构建一个按 Product下面的ProductCategory 对象中的 Name字段排序,在有了上面的经验之后,经过不断的goodle+单步调试,我们对排序扩展有了如下的改进

                      var methodName = isAscending ? "OrderBy" : "OrderByDescending";

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
    string[] props = property.Split('.');
    Type type = typeof(T);
    ParameterExpression arg = Expression.Parameter(type, "x");
    Expression expr = arg;
    foreach (string prop in props)
    {
        // use reflection (not ComponentModel) to mirror LINQ
        PropertyInfo pi = type.GetProperty(prop);
        expr = Expression.Property(expr, pi);
        type = pi.PropertyType;
    }
    Type delegateType = typeof(Func<,>).MakeGenericType(typeof(T), type);
    LambdaExpression lambda = Expression.Lambda(delegateType, expr, arg);
    object result = typeof(Queryable).GetMethods().Single(method => method.Name == methodName
                    && method.IsGenericMethodDefinition
                    && method.GetGenericArguments().Length == 2
                    && method.GetParameters().Length == 2)
                    .MakeGenericMethod(typeof(T), type)
                    .Invoke(null, new object[] { source, lambda });
    return (IOrderedQueryable<T>)result;
}

下面贴一个完整版的排序解决方案,包含一个对IEnumerable<T>的排序扩展

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
/// <summary>
    /// 排序类型
    /// </summary>
    public enum EOrderType
    {
        OrderBy = 0,
        OrderByDescending = 1,
        ThenBy = 2,
        ThenByDescending = 3
    }
 
    /// <summary>
    /// 排序扩展
    /// </summary>
    public static class OrderEx
    {
        /// <summary>
        /// 动态排序
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="source"></param>
        /// <param name="propertyName"></param>
        /// <param name="isAscending"></param>
        /// <returns></returns>
        public static IQueryable<T> OrderBy<T>(this IQueryable<T> source, string propertyName, bool isAscending) where T : class
        {
            //有关联属性
            if (propertyName.IndexOf('.') > 0)
            {
                if (isAscending)
                    return source.OrderBy(propertyName);
                else
                    return source.OrderByDescending(propertyName);
            }
            //简单属性
            else
            {
                var type = typeof(T);
                Expression resultExp = null;
                var property = type.GetProperty(propertyName);
                if (property == null)
                    throw new ArgumentException("propertyName", "不存在");
 
                var param = Expression.Parameter(type, "p");
                Expression propertyAccessExpression = Expression.MakeMemberAccess(param, property);
                var orderByExpression = Expression.Lambda(propertyAccessExpression, param);
                var methodName = isAscending ? "OrderBy" : "OrderByDescending";
                resultExp = Expression.Call(typeof(Queryable), methodName, new Type[] { type, property.PropertyType },
                                               source.Expression, Expression.Quote(orderByExpression));
                return source.Provider.CreateQuery<T>(resultExp);
            }
        }
 
        /// <summary>
        /// 升序排序
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="source"></param>
        /// <param name="property"></param>
        /// <returns></returns>
        public static IOrderedQueryable<T> OrderBy<T>(this IQueryable<T> source, string property)
        {
            return ApplyOrder<T>(source, property, EOrderType.OrderBy);
        }
 
        /// <summary>
        /// 降序排序
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="source"></param>
        /// <param name="property"></param>
        /// <returns></returns>
        public static IOrderedQueryable<T> OrderByDescending<T>(this IQueryable<T> source, string property)
        {
            return ApplyOrder<T>(source, property, EOrderType.OrderByDescending);
        }
 
        /// <summary>
        /// ThenBy
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="source"></param>
        /// <param name="property"></param>
        /// <returns></returns>
        public static IOrderedQueryable<T> ThenBy<T>(this IOrderedQueryable<T> source, string property)
        {
            return ApplyOrder<T>(source, property, EOrderType.ThenBy);
        }
 
        /// <summary>
        /// ThenByDescending
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="source"></param>
        /// <param name="property"></param>
        /// <returns></returns>
        public static IOrderedQueryable<T> ThenByDescending<T>(this IOrderedQueryable<T> source, string property)
        {
            return ApplyOrder<T>(source, property, EOrderType.ThenByDescending);
        }
 
        /// <summary>
        /// 应用排序
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="source"></param>
        /// <param name="property"></param>
        /// <param name="methodName"></param>
        /// <returns></returns>
        public static IOrderedQueryable<T> ApplyOrder<T>(this IQueryable<T> source, string property, EOrderType orderType)
        {
            var methodName = orderType.ToString();
 
            string[] props = property.Split('.');
            Type type = typeof(T);
            ParameterExpression arg = Expression.Parameter(type, "x");
            Expression expr = arg;
            foreach (string prop in props)
            {
                // use reflection (not ComponentModel) to mirror LINQ
                PropertyInfo pi = type.GetProperty(prop);
                expr = Expression.Property(expr, pi);
                type = pi.PropertyType;
            }
            Type delegateType = typeof(Func<,>).MakeGenericType(typeof(T), type);
            LambdaExpression lambda = Expression.Lambda(delegateType, expr, arg);
            object result = typeof(Queryable).GetMethods().Single(method => method.Name == methodName
                            && method.IsGenericMethodDefinition
                            && method.GetGenericArguments().Length == 2
                            && method.GetParameters().Length == 2)
                            .MakeGenericMethod(typeof(T), type)
                            .Invoke(null, new object[] { source, lambda });
            return (IOrderedQueryable<T>)result;
        }
 
 
        /// <summary>
        /// 动态排序
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="source"></param>
        /// <param name="propertyName"></param>
        /// <param name="isAscSort"></param>
        /// <returns></returns>
        public static IOrderedEnumerable<T> OrderBy<T>(this IEnumerable<T> source, string propertyName, bool isAscSort) where T : class
        {
            return ApplyOrder(source, propertyName, isAscSort ? EOrderType.OrderBy : EOrderType.OrderByDescending);
        }
 
        /// <summary>
        /// 处理排序
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="source"></param>
        /// <param name="property"></param>
        /// <param name="methodName"></param>
        /// <returns></returns>
        public static IOrderedEnumerable<T> ApplyOrder<T>(IEnumerable<T> source, string property, EOrderType orderType)
        {
            var methodName = orderType.ToString();
            string[] props = property.Split('.');
            Type type = typeof(T);
            ParameterExpression arg = Expression.Parameter(type, "x");
            Expression expr = arg;
            foreach (string prop in props)
            {
                // use reflection (not ComponentModel) to mirror LINQ
                PropertyInfo pi = type.GetProperty(prop);
                expr = Expression.Property(expr, pi);
                type = pi.PropertyType;
            }
            Type delegateType = typeof(Func<,>).MakeGenericType(typeof(T), type);
            LambdaExpression lambda = Expression.Lambda(delegateType, expr, arg);
 
            var orderMethod = typeof(Enumerable).GetMethods().Single(method => method.Name == methodName
                            && method.IsGenericMethodDefinition
                            && method.GetGenericArguments().Length == 2
                            && method.GetParameters().Length == 2);
 
            object result = orderMethod
                            .MakeGenericMethod(typeof(T), type)
                            .Invoke(null, new object[] { source, lambda.Compile() });
            return (IOrderedEnumerable<T>)result;
        }
    }
分类: .Net

随笔分类 -.Net

使用EF构建企业级应用(二)

2012-04-07 16:49 by 谢中涞, 463 visits, 网摘收藏编辑
摘要:动态排序扩展在上一节(使用EF构建企业级应用(一) ) 中,我们实现了数据库基本操作的CURD的定义,如果你直接复制这个代码到VS中编译,奇怪的问题就出现了,可能会出好几个错误,可能错误发生在类似这样的代码上”IQueryable<TEntity> query = Get(expression).OrderBy(orderPropertyName, isAscending);”,大致的错误可能是,这是为啥呢? 该不会是楼主忽悠吧,这个自然不会,且听如下分解.我们常使用的排序可能是如下样子:var temp=lst.OrderBy(t=>t.Code).ToList();因为后 阅读全文

使用EF构建企业级应用(一)

2012-04-07 13:29 by 谢中涞, 639 visits, 网摘收藏编辑
摘要:数据持久化最初接触EF是在2009年,那时因为EF还只支持DataBase-first模式,在项目中使用需要创建.edmx文件,因为觉得比较累赘,且生成的代码不容易控制,总觉得看着不爽,于是曾一度被抛弃,也总结了自己的一些数据持久化的东东,最近不经意间听说到了一个新词,code-first,觉得挺新潮,最开始还以为是微软出了个什么新技术,于是不停的Google,后得知原来所谓的code-first只是EF中的一部分,在潜心学习一段时间后,因为和Linq结合得非常好,感觉还挺喜欢这种模式的,于是,我们将这种模式应用在了我们新的项目上.在学习EF的时候,走过了很多弯路,也寻找了很多资料,现做一个小 阅读全文
原文地址:https://www.cnblogs.com/Leo_wl/p/2438240.html