Linq:IQueryable 扩展方法适用于 DBSet 但不适用于 ICollection
Linq: IQueryable extension methods works on DBSet but not on ICollection
我有一个包含该包的项目
EntityFramework6.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,我得到了启发,采用了这种替代方法来达到相同的结果:
我做了一个 "proxy class" 来获取过滤器,因为 Expression < Func < entityOne,bool> > 它是强类型的,我需要更通用的东西:
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()
希望对更新此内容有所帮助 post,感谢 Jon 启发我应用此解决方案
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()
因为您的可查询扩展的使用是 在 查询中并作用于不同类型的可查询,所以方法调用是传递给 Entity Framework 的表达式的一部分,它不知道 FullFilter()
做了什么,并且在这一点上窒息。
EF 无法将 FullFilter 方法转换为 SQL,因为您正在使用 LINQ to Entities。您可能需要使用 Linq to Objects
ctx.EntityTwo.ToList() 在你的方法之前,所以它首先获取你的对象列表,然后使用该列表执行你的方法,这样做你正在做 LINQ To Objects
我有一个包含该包的项目 EntityFramework6.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,我得到了启发,采用了这种替代方法来达到相同的结果:
我做了一个 "proxy class" 来获取过滤器,因为 Expression < Func < entityOne,bool> > 它是强类型的,我需要更通用的东西:
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()
希望对更新此内容有所帮助 post,感谢 Jon 启发我应用此解决方案
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()
因为您的可查询扩展的使用是 在 查询中并作用于不同类型的可查询,所以方法调用是传递给 Entity Framework 的表达式的一部分,它不知道 FullFilter()
做了什么,并且在这一点上窒息。
EF 无法将 FullFilter 方法转换为 SQL,因为您正在使用 LINQ to Entities。您可能需要使用 Linq to Objects
ctx.EntityTwo.ToList() 在你的方法之前,所以它首先获取你的对象列表,然后使用该列表执行你的方法,这样做你正在做 LINQ To Objects