EntityFramework v1 .Net3.5 Sp1 动态查询过滤检索所遇到的问题

    最近在封装EntityFramework的时候碰到许多问题,下面就做一个简短的笔记. 

 泛型推断

     例如Save<T>(T o)这样的方法是没有问题的,但是如果参数再包裹一层泛型就不行了,Save<T>(IEnumerable<T> o),所以只能在方法体类判断是否实现了IEnumerable<T>接口,然后分情况处理.关于更多C#3.0中的泛型推断.大体上就是说C#编译器只能推断出明确的一种类型,而不能同时包含多种,比如上面的Save<T>(IEnumerable<T> o)则编译器无法得知参数的具体类型,就需要指定类型.

 拓展方法的访问级别

     基本上就是一个外部的类,除非在同一程序集里并设定被拓展对象为internal访问级别才能访问,否则protected都访问不到.

  封装EntityFremawork操作

     这里主要讲讲查询,一般来说Find方法的签名如下

public override List<T> Find<T>(int start, int len = 10, IWhereItem<T> whereitem = null, IOrderItem<T> orders = nullbool isDetach = true)

     起始元素,页长度,过滤语句,排序语句,最后一个是EntityFramework独有,是否与上下文对象解绑.
     在构造ObjectQuery时就需要获得EF的映射关系,用System.Data.Metadata.Edm读取映射信息就可以了,这里我就直接将代码贴出来了.接受一个EntityContainer就可以不断的读取信息了,本身继承一个见到那的缓存类,实际上就是对字典的一个简单封装.

    public class EdmInfo
    {
        
public EntitySetBase EntitySetBase { getset; }
        
public string TableName { getset; }
        
public string TypeName { getset; }
        
public string KeyName { getset; }
    }
    
public class EdmInfoManager : SimpleCache<Type, EdmInfo>
    {
        
public EntityContainer RootContainer { getprivate set; }
        
public EdmInfoManager(EntityContainer ec)
        {
            RootContainer 
= ec;
        }
        
public new EdmInfo this[Type T]
        {
            
get
            {
                
if(!ContainsKey(T))
                {
                    EdmInfo edm 
= new EdmInfo();
                    edm.EntitySetBase 
= typeToEntitySetBase(T);
                    edm.TableName 
= edm.EntitySetBase.Name;
                    edm.TypeName 
= T.Name;
                    edm.KeyName 
= entitySetBaseToKeyName(edm.EntitySetBase);
                    
base[T] = edm;
                    
return edm;
                }
                
return base[T];
            }
            
set
            {
                
base[T] = value;
            }
        }
        
private EntitySetBase typeToEntitySetBase(Type t)
        {
            
return RootContainer.BaseEntitySets
                       .Where(bes 
=> bes.ElementType.Name == t.Name)
                       .FirstOrDefault();
        }
        
private string entitySetBaseToKeyName(EntitySetBase es)
        {
            
return es.ElementType.KeyMembers[0].Name;
        }
    }

     然后就是排序项,由于项目有一些lambda表达式风格的排序,以及普通属性字符串似的排序,所以又做了一个简单封装,IOrderItem<T>结构下有两个实现StringOrderItem与ExpressionOrderItem.主要是StringOrderItem,因为最后都将排序信息保存为字符串了,所以只能以字符串形式排序,这就为后面造成了一些人为的障碍.
     过滤项就是封装了一下Expression<Func<T, bool>>,不过提供从字符串构造的方法.最后都是Lambda表达式的形式存储.
     最后检索的时候就碰到问题了,ObjectQuery.Where(Expression<Func<T, bool>> exp)是一个拓展方法,返回的不是ObjectQuery,导致了后面无法使用OrderBy(String orderstring).只能使用OrderBy的拓展方法Lambda表达式形式.卡壳了很久,找了一些资料.其中不乏微软的EF拓展库.有个拓展方法能够强制转换为ObjectQuery在链式操作中,但是在我这里一旦强转后面的操作都报错,大概是只支持EFv4吧.

    能否将IWhereItem里的Lambda表达式转为ESQL语句呢?.试着重写一个ExpressionVisitor可惜没功夫没到家,表达式的资料到是找到了不少.就是没写出来.

    表达式的各种构造 : http://rednaxelafx.javaeye.com/blog/247270

    LinqToSQL : 一种Linq到TSQL语句的转换,地址忘记了,代码在此

     在stackoverflow的提问 :  http://stackoverflow.com/questions/3305224/problems-with-entity-framework-queries

     就想把Order转为Lambda表达式形式,但是如何泛型如何设置呢?Expression<Func<T, object>>在面对Expression<Func<T, int>>时会自动有个转换过程,详见Lambda传参自动包装.所以无法直接使用Expression<Func<T, object>>类型传递,在动态构造Linq过滤,排序时发现了一个解决方法,使用IOrderedQueryable包装过滤项,继续搜索,终于找到了使用泛型反射出类型,然后动态构造Lambda表达式(且指定类型)的解决方法了.

 

private static IOrderedEnumerable<T> SortEngine<T>(this IEnumerable<T> source, string columnName, bool isAscending, bool started)
{
    var item 
= Expression.Parameter(typeof(T), "item");
    var propertyValue 
= Expression.PropertyOrField(item, columnName);
    var propertyLambda 
= Expression.Lambda(propertyValue, item);
    
// item => item.{columnName}
    
    var sourceExpression 
= Expression.Parameter(typeof(IEnumerable<T>), "source");

    
string methodName;
    Expression inputExpression;
    
if (started)
    {
        methodName 
= isAscending ? "ThenBy" : "ThenByDescending";
        inputExpression 
= Expression.Convert(sourceExpression, typeof(IOrderedEnumerable<T>));
        
// ThenBy requires input to be IOrderedEnumerable<T>
    }
    
else
    {
        methodName 
= isAscending ? "OrderBy" : "OrderByDescending";
        inputExpression 
= sourceExpression;
    }

    var sortTypeParameters 
= new Type[] { typeof(T), propertyValue.Type };
    var sortExpression 
= Expression.Call(typeof(Enumerable), methodName, sortTypeParameters, inputExpression, propertyLambda);
    var sortLambda 
= Expression.Lambda<Func<IEnumerable<T>, IOrderedEnumerable<T>>>(sortExpression, sourceExpression);
    
// source => Enumerable.OrderBy<T, TKey>(source, item => item.{columnName})
    
    
return sortLambda.Compile()(source);
}

public static IOrderedEnumerable<T> OrderBy<T>(this IEnumerable<T> source, string columnName)
{
    
return SortEngine(source, columnName, truefalse);
}

public static IOrderedEnumerable<T> OrderByDescending<T>(this IEnumerable<T> source, string columnName)
{
    
return SortEngine(source, columnName, falsefalse);
}

public static IOrderedEnumerable<T> ThenBy<T>(this IOrderedEnumerable<T> source, string columnName)
{
    
return SortEngine(source, columnName, truetrue);
}

public static IOrderedEnumerable<T> ThenByDescending<T>(this IOrderedEnumerable<T> source, string columnName)
{
    
return SortEngine(source, columnName, falsetrue);
}


    最后Find方法全貌

        public override List<T> Find<T>(int start, int len = 10, IWhereItem<T> whereitem = null, IOrderItem<T> orders = nullbool isDetach = true)
        {
            var context 
= getInstence();
            var edminfo 
= EdmMgr[typeof (T)];
            ObjectQuery
<T> query = context.CreateQuery<T>("[" + edminfo.TableName + "]");
            query 
= MemerVisitor.IncludeMembers.Aggregate(query, (current, str) => current.Include(str));
            orders 
= orders ?? OrderItemFactry.Get<T>(edminfo.KeyName, true);
            List
<T> result;

            result 
= whereitem == null || whereitem.Body == null
                         
? start == -1
                               
? query.Skip(orders.OrderString, "0").ToList()
                               : query.Skip(orders.OrderString, start.ToString()).Take(len).ToList()
                         : start 
== -1
                               
? query.Skip(orders.OrderString, "0").Where(whereitem.Body).ToList()
                               : query.Skip(orders.OrderString, 
"0").Where(whereitem.Body).OrderBy(orders.Property).Skip(start).Take(len).ToList();

            
if (isDetach && MemerVisitor.IncludeMembers.Count == 0)
                result.ForEach(x 
=>
                                   {
                                       
try
                                       {
                                           context.Detach(x);
                                       }
                                       
catch (InvalidOperationException)
                                       {
                                       }
                                   });
            MemerVisitor.IncludeMembers.Clear();
            
return result;
        }


 

posted @ 2010-07-23 13:07  Dreampuf  阅读(874)  评论(0编辑  收藏  举报