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  =   null bool  isDetach  =   true )

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

     public   class  EdmInfo
    {
        
public  EntitySetBase EntitySetBase {  get set ; }
        
public   string  TableName {  get set ; }
        
public   string  TypeName {  get set ; }
        
public   string  KeyName {  get set ; }
    }
    
public   class  EdmInfoManager : SimpleCache < Type, EdmInfo >
    {
        
public  EntityContainer RootContainer {  get private   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,  true false );
}

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

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

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


    最后Find方法全貌

         public   override  List < T >  Find < T > ( int  start,  int  len  =   10 , IWhereItem < T >  whereitem  =   null , IOrderItem < T >  orders  =   null bool  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;
        }


 

转载于:https://www.cnblogs.com/dreampuf/archive/2010/07/23/Problems_With_Entity_Framework_Queries.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值