Linq:IQueryable扩展方法适用于DBSet,但不适用于ICollection

时间:2018-11-20 14:09:01

标签: c# entity-framework extension-methods

我有一个带有包装的项目 EntityFramework 6.1.0

我正在使用DB-First模型

某些模型实体已通过以下方式扩展:

public interface IVersionable{
int VersionId{get;set;}
}
public interface IEditable{
bool IsEditable{get;set;}
}
public interface IFullFeatures:IVersionable,IEditable{}
public partial EntityOne:IFullFeatures{
   //This is the extension partial class for the auto-generated model class EntityOne that already has interface properties
}
public partial EntityTwo:IFullFeatures{
   //This is the extension partial class for the auto-generated model class EntityTwo that already has interface properties
}

自动生成的类EntityOne和EntityTwo具有IFullFeatures所需的所有属性,对于EntityTwo自动生成的文件,我们具有以下ICollection:

public virtual ICollection<EntityOne> EntityOne {get;set;}

最后我有了扩展方法:

public static class FeaturesExtensionMethod{
     public static IQueryable<T> FilterEditable<T>(this IQueryable<T> source) where T:class,IEditable{
         return source.Where(s=>s.IsEditable);
     }
     public static IQueryable<T> FilterVersion<T>(this IQueryable<T> source, int versionId) where T:class,IVersionable{
         return source.Where(s=>s.VersionId==versionId);
     }
     public static IQueryable<T> FullFilter<T>(this IQueryable<T> source, int versionId) where T:class,IVersionable{
         return source.FilterEditable().FilterVersion(versionId);
     }
}

然后,在运行时我执行以下操作:

var everyEntitiTwo=ctx.EntityTwo.FullFilter(4).ToList();

没问题,它工作正常并且可以过滤...但是在运行时,我改为执行此操作:

var test= ctx.EntityTwo.Include("EntityOne").Select(et=>et.EntityOne.AsQueryAble().FullFilter(4)).ToList()

我收到此错误:

LINQ to Entities无法识别方法'FullFilter',并且该方法无法转换为商店表达式。

所以问题是:我的扩展方法有什么问题?为什么在第二种情况下甚至在第一种情况下都不会出现此错误?

谢谢。

更新

多亏了乔恩·汉纳(Jon Hanna),我才从这种替代方法中获得了灵感,从而获得了相同的结果:

我创建了一个“代理类”来获取过滤器,因为表达式>是强类型的,并且我需要一些更通用的东西:

public static FilterProxies{
    public static GetProxiedFilter<T>(int versionId, bool onlyEditable) where T: class, IFullFeatures{
       Expression<Func<T,bool>> filteredExp
       if(onlyEditable){
          filteredExp=(iff=>iff.VersioneId==versionId&&iff.IsEditable);
       }
       else{
          filteredExp=(iff=>iff.VersioneId==versionId); 
       }
       return filteredExp;
   }
}

然后,使用情况:

var filter=FilterProxies.GetProxiedFilter<EntityOne>(4,true);
var test= ctx.EntityTwo.Include("EntityOne").Select(et=>et.EntityOne.AsQueryAble().Where(filter)).ToList()

希望能对这篇文章有所帮助,这要感谢乔恩(Jon)启发我应用此解决方案

2 个答案:

答案 0 :(得分:1)

ctx.EntityTwo.FullFilter(4).ToList();

这立即变为

ctx.EntityTwo.Where(s => s.IsEditable).Where(s => s.VersionId == 4).ToList();

Entity Framework当然可以处理。

var test= ctx.EntityTwo.Include("EntityOne").Select(et=>et.EntityOne.AsQueryAble().FullFilter(4)).ToList()

由于在查询中使用使用了可查询扩展,并且作用于另一种可查询类型,因此方法调用是传递给实体框架的表达式的一部分,并且它不知道FullFilter()会这样做,并在这一点上令人窒息。

答案 1 :(得分:0)

EF无法将FullFilter方法转换为SQL,因为您正在使用LINQ to Entities。您可能需要使用Linq to Objects

ctx.EntityTwo.ToList()在您的方法之前,因此它首先获取对象列表,然后使用该列表执行您的方法,即执行LINQ To Objects