Linq - 在子属性上重用表达式

时间:2010-03-15 13:17:39

标签: linq lambda

不确定我尝试的是否可行,但我想在对象父属性上重用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
    }
}

感谢所有人的帮助。

1 个答案:

答案 0 :(得分:1)

你是在思考它。

首先,不要返回Expression<Func<Parent, bool>>,这将要求您编译表达式。只需返回Func<Parent, bool>即可。

接下来,你可以称之为:

 context.Children.Where(c => c.Name == "bar" && ParentQuery()(c.Dad));

 context.Parents.Where(ParentQuery());