不确定我尝试的是否可行,但我想在对象父属性上重用linq表达式。
使用给定的类:
class Parent {
int Id { get; set; }
IList<Child> Children { get; set; }
string Name { get; set; }
}
class Child{
int Id { get; set; }
Parent Dad { get; set; }
string Name { get; set; }
}
如果我有帮助者
Expression<Func<Parent,bool> ParentQuery() {
Expression<Func<Parent,bool> q = p => p.Name=="foo";
}
然后我想在为孩子查询数据时使用它,如下所示:
using(var context=new Entities.Context) {
var data=context.Child.Where(c => c.Name=="bar"
&& c.Dad.Where(ParentQuery));
}
我知道我可以在儿童收藏中这样做:
using(var context=new Entities.Context) {
var data=context.Parent.Where(p => p.Name=="foo"
&& p.Childen.Where(childQuery));
}
但是无法在不属于集合的属性上看到任何方法 这只是一个简化的例子,实际上ParentQuery会更复杂,我想避免在多个地方重复这个而不是只有2层我会接近5或6,但所有这些都需要引用父查询以确保安全性。
如果这是不可能的,我的另一个想法是以某种方式将ParentQuery表达式转换为给定类型如此有效: p =&gt; p.Name == “foo” 的; 变成: c =&gt; c.Dad.Name == “foo” 的; 但是使用泛型/其他形式的查询构建器允许它保留父查询,然后只需要为每个子对象构建一个翻译器,该子对象在属性路由中替换为父对象。
编辑: 继续@David morton的评论
最初看起来我可以从Expression更改为委托函数然后调用 。凡(ParentQuery()(c.Dad));
但是我在更广泛的存储库模式中使用它,并且无法看到我如何将它与泛型和谓词构建器一起使用 - 我不想从存储中检索行并在客户端上过滤(在本例中为Web服务器)。我有一个通用的get数据方法,它接受基表达式查询。然后我想测试一下,看看提供的类型是否实现了ISecuredEntity,以及它是否为我们正在处理的实体附加了securityQuery。
public static IList<T> GetData<T >(Expression<Func<T, bool>> query) {
IList<T> data=null;
var secQuery=RepositoryHelperers.GetScurityQuery<T>();
if(secQuery!=null) {
query.And(secQuery);
}
using(var context=new Entities.Context()) {
var d=context.GetGenericEntitySet<T>();
data=d.ToList();
}
return data;
}
ISecuredEntity:
public interface ISecuredEntity : IEntityBase {
Expression<Func<T, bool>> SecurityQuery<T>();
}
示例实体:
public partial class ExampleEntity: ISecuredEntity {
public Expression<Func<T, bool>> SecurityQuery<T>() {
//get specific type expression and make generic
Type genType = typeof(Func<,>).MakeGenericType(typeof(ExampleEntity), typeof(bool));
var q = this.SecurityQuery(user);
return (Expression<Func<T, bool>>)Expression.Lambda(genType, q.Body, q.Parameters);
}
public Expression<Func<ExampleEntity, bool>> SecurityQuery() {
return e => e.OwnerId==currentUser.Id;
}
}
和repositoryHelpers:
internal static partial class RepositoryHelpers {
internal static Expression<Func<T, bool>> SecureQuery<T>() where T : new() {
var instanceOfT = new T();
if (typeof(Entities.ISecuredEntity).IsAssignableFrom(typeof(T))) {
return ((Entities.ISecuredEntity)instanceOfT).SecurityQuery<T>();
}
return null;
}
}
编辑这是(最终)解决方案
我最终回到使用表达式,并使用LinqKit Invoke。注意:对于EF,我还必须在entitySet上调用.AsExpandable()
关键部分是能够致电:
Product.SecureFunction(user).Invoke(pd.ParentProduct);
这样我就可以将上下文传递给我的父查询
我的结束课程如下:
public interface ISecureEntity {
Func<T,bool> SecureFunction<T>(UserAccount user);
}
public class Product : ISecureEntity {
public Expression<Func<T,bool>> SecureFunction<T>(UserAccount user) {
return SecureFunction(user) as Expression<Func<T,bool>>;
}
public static Expression<Func<Product,bool>> SecureFunction(UserAccount user) {
return f => f.OwnerId==user.AccountId;
}
public string Name { get;set; }
public string OwnerId { get;set; }
}
public class ProductDetail : ISecureEntity {
public Expression<Func<T,bool>> SecureFunction<T>(UserAccount user) {
return SecureFunction(user) as Expression<Func<T,bool>>;
}
public static Func<ProductDetail,bool> SecureFunction(UserAccount user) {
return pd => Product.SecureFunction(user).Invoke(pd.ParentProduct);
}
public int DetailId { get;set; }
public string DetailText { get;set; }
public Product ParentProduct { get;set; }
}
用法:
public IList<T> GetData<T>() {
IList<T> data=null;
Expression<Func<T,bool>> query=GetSecurityQuery<T>();
using(var context=new Context()) {
var d=context.GetGenericEntitySet<T>().Where(query);
data=d.ToList();
}
return data;
}
private Expression<Func<T,bool>> GetSecurityQuery<T>() where T : new() {
var instanceOfT = new T();
if (typeof(Entities.ISecuredEntity).IsAssignableFrom(typeof(T))) {
return ((Entities.ISecuredEntity)instanceOfT).SecurityQuery<T>(GetCurrentUser());
}
return a => true; //returning a dummy query
}
}
感谢所有人的帮助。
答案 0 :(得分:1)
你是在思考它。
首先,不要返回Expression<Func<Parent, bool>>
,这将要求您编译表达式。只需返回Func<Parent, bool>
即可。
接下来,你可以称之为:
context.Children.Where(c => c.Name == "bar" && ParentQuery()(c.Dad));
context.Parents.Where(ParentQuery());