【问题标题】:How to include an And() Expression that checks for a Property and it's value如何包含检查属性及其值的 And() 表达式
【发布时间】:2011-01-24 12:33:57
【问题描述】:

我想在我们的存储库中添加一个检查,以过滤掉 companyId 上的所有对象(如果它存在并且是否与给定值匹配)。

所以我们有:

public T First<T>(Expression<Func<T, bool>> expression) where T : EntityObject  
{  
   var set = GetObjectSet<T>();  
   return set.FirstOrDefault<T>();  
}  

我想添加一些看起来在哪里...

express.And("Check for CompanyId property if it exists then make sure it = 3");  

关于如何解决这个问题的任何想法?
谢谢:)

【问题讨论】:

    标签: entity-framework lambda expression expression-trees


    【解决方案1】:

    如果您正在寻找一个可以用来附加公司 ID 的函数,如果实体上存在公司 ID,请检查您的表达式,这应该可以解决问题:

    public static Expression<Func<T, bool>> CheckPropertyIfExists<T, TProperty>(Expression<Func<T, bool>> expression, string propertyName, TProperty propertyValue)
    {
        Type type = typeof(T);
        var property = type.GetProperty(propertyName, typeof(TProperty));
        if(property == null || !property.CanRead)
            return expression;
    
        return expression.Update(
            Expression.And( // && 
                Expression.Equal( // ==
                    Expression.MakeMemberAccess(expression.Parameters[0], property), // T parameter.{propertyName}
                    Expression.Constant(propertyValue) // specified propertyValue constant
                ),
                expression.Body // Original expression
            ),
            expression.Parameters
        );
    }
    

    你可以这样使用它:

    public T First<T>(Expression<Func<T, bool>> expression, int companyId)
    {
        var set = GetObjectSet<T>();  
        return set.FirstOrDefault<T>(CheckPropertyIfExists(expression, "CompanyId", companyId));  
    }
    

    现在您可以使用您的表达式和您想要过滤的 companyId 调用您的 First 方法。

    更好的方法可能是将其用作过滤方法,即将其重写为不需要内部表达式并适用于对象查询(或 IQueryable)的扩展方法:

    public static ObjectQuery<T> FilterByPropertyIfExists<T, TProperty>(this ObjectQuery<T> query, string propertyName, TProperty propertyValue)
    {
        Type type = typeof(T);
        var property = type.GetProperty(propertyName, typeof(TProperty));
        if(property == null || !property.CanRead)
            return query;
    
        var parameter = Expression.Parameter(typeof(T), "x");
        Expression<Func<T, bool>> predicate = (Expression<Func<T, bool>>)Expression.Lambda(
            Expression.Equal( // ==
                Expression.MakeMemberAccess(parameter, property), // T parameter.{propertyName}
                Expression.Constant(propertyValue) // specified propertyValue constant
            ),
            parameter
        );
        return query.Where(predicate);
    }
    

    它的美妙之处在于它可以很好地与标准 LINQ 语法(查询和流利)一起工作。

    例如它允许这样的查询:

    from x in repository.Clients.FilterByPropertyIfExists("Company", 5)
    where x == ???
    select x.Name;
    

    [编辑]

    我已经对其进行了一些清理,并添加了对参数可见性(必须是公共的静态属性)以及 ObjectQuery 实现(将自动用于 ObjectQuery 和 ObjectSet)的检查:

    public static class QueryExtensions
    {
        public static IQueryable<T> FilterByPropertyIfExists<T, TProperty>(this IQueryable<T> query, string propertyName, TProperty propertyValue)
        {
            Type type = typeof(T);
            var property = type.GetProperty(
                propertyName, 
                BindingFlags.Instance | BindingFlags.Public, // Must be a public instance property
                null, 
                typeof(TProperty), // Must be of the correct return type
                Type.EmptyTypes, // Can't have parameters
                null
            );
            if (property == null || !property.CanRead) // Must exist and be readable
                return query; // Return query unchanged
    
            // Create a predicate to pass to the Where method
            var parameter = Expression.Parameter(typeof(T), "it");
            Expression<Func<T, bool>> predicate = (Expression<Func<T, bool>>)Expression.Lambda(
                Expression.Equal( // ==
                    Expression.MakeMemberAccess(parameter, property), // T parameter.{propertyName}
                    Expression.Constant(propertyValue) // specified propertyValue constant
                ),
                parameter
            );
            return query.Where(predicate); // Filter the query
        }
    
        public static ObjectQuery<T> FilterByPropertyIfExists<T, TProperty>(this ObjectQuery<T> query, string propertyName, TProperty propertyValue)
        {
            var filteredQuery = FilterByPropertyIfExists((IQueryable<T>)query, propertyName, propertyValue);
            return (ObjectQuery<T>)filteredQuery; // Cast filtered query back to an ObjectQuery
        }
    }
    

    【讨论】:

    • 哇,这看起来很有趣!您认为它也适用于 LINQ to Entities 吗?例如:使用您的CheckPropertyIfExists 作为FirstOrDefault 的谓词,这在LINQ to Entities 中是否有效? (我的理解是谓词必须可以转换为 SQL 语句,并且我只能在谓词内部使用“规范函数”,但我不太确定这一切是如何详细进行的。)
    • 是的,我已经对其进行了测试,它适用于 LINQ to Entities。它也应该与 LINQ to SQL 一起使用。它仍然可以翻译成 SQL 语句,因为它没有引入任何没有出现在任何简单 where 子句中的东西。不过,它仍然受与 L2E 相同的规则约束,即它不适用于 enum 值。
    • 太棒了!当我有一点时间时,我会玩这个。我不知道Expression 类提供了这么多强大的功能。
    • 是的,表达式树确实很棒,但一开始很难理解。 :)
    【解决方案2】:

    看起来是一个远离反射的界面的好候选:

    public interface ICompanyFilterable
    {
      int CompanyId { get; set; }
    }
    
    public partial class YourEntity : ICompanyFilterable
    {
        ....
    }
    
    public static IQueryable<T> FilterByCompanyId<T>(this IQueryable<T> query, int companyId)
        where T : ICompanyFilterable
    {
        return query.Where(e => e.CompanyId == companyId);
    }
    

    【讨论】:

      【解决方案3】:

      本诺, 非常感谢您的帖子非常有用。

      我采用了您在其中放入的内容并创建了一个扩展方法,该方法将“And”过滤器附加到现有表达式。

          public static Expression<Func<T, bool>> AddEqualityCheck<T, TProperty>(this Expression<Func<T, bool>> expression, string propertyName, TProperty propertyValue)
          {
              Type type = typeof(T);
              var property = type.GetProperty(
                  propertyName,
                  BindingFlags.Instance | BindingFlags.Public,
                  null,
                  typeof(TProperty),
                  Type.EmptyTypes,
                  null
              );
      
              if (property == null || !property.CanRead)
              {
                  return expression;
              }
              else
              {
                  var equalityExpression = Expression.Equal(
                      Expression.MakeMemberAccess(expression.Parameters[0], property),
                      Expression.Constant(propertyValue)
                      );
                  var andEqualityExpression = Expression.And(equalityExpression, expression.Body);
                  return expression.Update(andEqualityExpression, expression.Parameters);
              }
          }
      

      【讨论】:

      • 酷,但这正是我回答中的第一段代码所做的;)
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-07-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多