【问题标题】:Linq - reuse expression on child propertyLinq - 在子属性上重用表达式
【发布时间】:2010-03-18 21:20:00
【问题描述】:

不确定我正在尝试的是否可行,但我想在对象父属性上重用 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 => p.Name=="foo"; 变成: c => c.Dad.Name=="foo"; 但是使用泛型/某种其他形式的查询构建器,允许它保留父查询,然后只需要为每个子对象构建一个翻译器,该翻译器在属性路由中替换到父对象。

编辑: 继@David morton 的 cmets

最初看起来我可以从表达式更改为委托函数,然后调用 .Where(ParentQuery()(c.Dad));

但是我在更广泛的存储库模式中使用它,并且看不到如何将它与泛型和谓词构建器一起使用 - 我不想从存储中检索行并在客户端(在本例中为 Web 服务器)进行过滤。我有一个接受基本表达式查询的通用获取数据方法。然后我想测试一下提供的类型是否实现了 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;

    }
}

和存储库助手:

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
    }
}

感谢大家的帮助。

【问题讨论】:

    标签: linq lambda


    【解决方案1】:

    你想多了。

    首先,不要返回Expression&lt;Func&lt;Parent, bool&gt;&gt;,这需要您编译表达式。而是简单地返回一个Func&lt;Parent, bool&gt;

    接下来,看你怎么称呼它了:

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

    【讨论】:

    • 啊 - 太好了,几乎可以满足我的要求。我正在使用 LinqKut 在 ParentQuery 中建立 epression,因此 Expression
    • 好的,差不多了。在我的实现中,ParentQuery 实际上在接口上声明为 public Func SecurityQuery() 之前我然后通过构建一个强类型查询返回通过 Expression.Lambda() 的泛型表达式。如果我有 public Func SecurityQuery() 如何将其转换为 Func ?确定有一种我想念的简单方法吗??
    • 已经做了更多的测试,这是我的最后一个障碍 - 如何将 Func 转换为 Func (在运行时,T 将是父级)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-07-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多