修改 Lambda 表达式

发布于 2024-10-12 16:59:20 字数 1603 浏览 3 评论 0原文

我正在使用 NHibernate 3.0 开发一个应用程序。我开发了一个存储库,它接受表达式来使用 QueryOver 进行一些过滤。我的方法是这样的:

public IEnumerable<T> FindAll(Expression<Func<T, bool>> filter) {
   return Session.QueryOver<T>().Where(filter).List();
}

效果很好。因此,我也有一个服务层,并且该服务中的我的方法接受基元类型,如下所示:

public IEnumerable<Product> GetProducts(string name, int? stock, int? reserved) {

  // how init the expression ?    
  Expression<Func<Product, bool>> expression = ???;

  if (!string.IsNullOrEmpty(name)) {
     //add AND condition for name field in expression
  }  
  if (stock.HasValue) {
     //add AND condition for stock field in expression
  }
  if (reserved.HasValue) {
     //add AND condition for reserved field in expression
  }

  return _repository.FindAll(expression);
}

我的疑问是:

这可能吗?必要时添加一些条件(当我的参数有价值时)?

谢谢

/// 我的编辑

public ActionResult Index(ProductFilter filter) {
   if (!string.IsNullOrEmpty(filter.Name) {
      return View(_service.GetProductsByName(filter.Name))
   }

   // others  conditions
}

/// 几乎是一个解决方案

Expression<Func<Product, bool>> filter = x => true;

if (!string.IsNullOrEmpty(name))
    filter = x => filter.Compile().Invoke(x) && x.Name == name;

if (stock.HasValue) 
    filter = x => filter.Compile().Invoke(x) && x.Stock == stock.Value;

if (reserved.HasValue)
    filter = x => filter.Compile().Invoke(x) && x.Reserved == reserved.Value;

return _repository.FindAll(filter);

I'm developing an application with NHibernate 3.0. I have developed a Repository hat accept a expression to do some filter with QueryOver. My method is something like this:

public IEnumerable<T> FindAll(Expression<Func<T, bool>> filter) {
   return Session.QueryOver<T>().Where(filter).List();
}

It works fine. So, I have a Service layer as well, and My methods in this services accepts primitives types, like this:

public IEnumerable<Product> GetProducts(string name, int? stock, int? reserved) {

  // how init the expression ?    
  Expression<Func<Product, bool>> expression = ???;

  if (!string.IsNullOrEmpty(name)) {
     //add AND condition for name field in expression
  }  
  if (stock.HasValue) {
     //add AND condition for stock field in expression
  }
  if (reserved.HasValue) {
     //add AND condition for reserved field in expression
  }

  return _repository.FindAll(expression);
}

My doubts are:

Is it possible ? Ta add some conditions when necessary (when my parameters has value) ?

Thanks

/// my edits

public ActionResult Index(ProductFilter filter) {
   if (!string.IsNullOrEmpty(filter.Name) {
      return View(_service.GetProductsByName(filter.Name))
   }

   // others  conditions
}

/// Almost a solution

Expression<Func<Product, bool>> filter = x => true;

if (!string.IsNullOrEmpty(name))
    filter = x => filter.Compile().Invoke(x) && x.Name == name;

if (stock.HasValue) 
    filter = x => filter.Compile().Invoke(x) && x.Stock == stock.Value;

if (reserved.HasValue)
    filter = x => filter.Compile().Invoke(x) && x.Reserved == reserved.Value;

return _repository.FindAll(filter);

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

夏末染殇 2024-10-19 16:59:20

这是一种方法来做到这一点。我不会对你正在做的事情进行编辑 - 它看起来像是通过示例查询,这几乎总是有问题的。这是这里其他人最好避免的。不过这个表达方式很有趣——所以我认为值得一试。

class MyClass
{
     public string Name { get; set; }
     public bool Hero { get; set; }
     public int Age { get; set; }
}

我们想像这样查询它:

   string name = null;
   int? age = 18;
   Expression<Func<MyClass, bool>> myExpr = 
      x => (string.IsNullOrEmpty(name) || x.Name == name) && 
           (!age.HasValue || x.Age > (age ?? 0));
   myExpr = myExpr.RemoveCloture(); // this line here - removes the cloture - 
               // and replaces it with constant values - and shortcuts 
               // boolean evaluations that are no longer necessary.
               // in effect this expression now becomes :
               // x => x.Age > 18
   bool result = myExpr.Compile()(
      new MyClass {Name = "Rondon", Hero = true, Age = 92});

所以你所要做的就是编写 RemoveCloture(); - 这不是问题。

// using System;
// using System.Linq.Expressions;

public static class ClotureRemover
{

#region Public Methods

public static Expression<TExpressionType> RemoveCloture<TExpressionType>(
    this Expression<TExpressionType> e)
{
    var converter = new RemoveClotureVisitor();
    var newBody = converter.Visit(e.Body);
    return Expression.Lambda<TExpressionType>(newBody, e.Parameters);
}

#endregion

private class RemoveClotureVisitor : ExpressionVisitor
{


    public RemoveClotureVisitor()
    {
    }


    public override Expression Visit(Expression node)
    {
        if (!RequiresParameterVisitor.RequiresParameter(node))
        {
            Expression<Func<object>> funct = () => new object();
            funct = Expression.Lambda<Func<object>>(Expression.Convert(node, typeof(object)), funct.Parameters);
            object res = funct.Compile()();
            return ConstantExpression.Constant(res, node.Type);
        }
        return base.Visit(node);
    }


    protected override Expression VisitBinary(BinaryExpression node)
    {
        if ((node.NodeType == ExpressionType.AndAlso) || (node.NodeType == ExpressionType.OrElse))
        {
            Expression newLeft = Visit(node.Left);
            Expression newRight = Visit(node.Right);

            bool isOr = (node.NodeType == ExpressionType.OrElse);
            bool value;
            if (IsBoolConst(newLeft, out value))
            {
                if (value ^ isOr)
                {
                    return newRight;
                }
                else
                {
                    return newLeft;
                }
            }

            if (IsBoolConst(newRight, out value))
            {
                if (value ^ isOr)
                {
                    return newLeft;
                }
                else
                {
                    return newRight;
                }
            }
        }
        return base.VisitBinary(node);
    }

    protected override Expression VisitUnary(UnaryExpression node)
    {
        if (node.NodeType == ExpressionType.Convert || node.NodeType == ExpressionType.ConvertChecked)
        {
            Expression newOpperand = Visit(node.Operand);
            if (newOpperand.Type == node.Type)
            {
                return newOpperand;
            }
        }
        return base.VisitUnary(node);
    }

    private static bool IsBoolConst(Expression node, out bool value)
    {
        ConstantExpression asConst = node as ConstantExpression;
        if (asConst != null)
        {
            if (asConst.Type == typeof(bool))
            {
                value = (bool)asConst.Value;
                return true;
            }
        }
        value = false;
        return false;
    }
}

private class RequiresParameterVisitor : ExpressionVisitor
{
    protected RequiresParameterVisitor()
    {
        result = false;
    }

    public static bool RequiresParameter(Expression node)
    {
        RequiresParameterVisitor visitor = new RequiresParameterVisitor();
        visitor.Visit(node);
        return visitor.result;
    }

    protected override Expression VisitParameter(ParameterExpression node)
    {
        result = true;
        return base.VisitParameter(node);
    }

    internal bool result;
}

}

Here is a way to do this. I am not going to editorialize on what you are doing - it looks like query by example, which is almost always problematic. It is as the others here have best avoided. The expression thing is interesting though - so I thought it was worth a crack at it.

class MyClass
{
     public string Name { get; set; }
     public bool Hero { get; set; }
     public int Age { get; set; }
}

And we want to query it like this:

   string name = null;
   int? age = 18;
   Expression<Func<MyClass, bool>> myExpr = 
      x => (string.IsNullOrEmpty(name) || x.Name == name) && 
           (!age.HasValue || x.Age > (age ?? 0));
   myExpr = myExpr.RemoveCloture(); // this line here - removes the cloture - 
               // and replaces it with constant values - and shortcuts 
               // boolean evaluations that are no longer necessary.
               // in effect this expression now becomes :
               // x => x.Age > 18
   bool result = myExpr.Compile()(
      new MyClass {Name = "Rondon", Hero = true, Age = 92});

So all you have to do is write RemoveCloture(); - not a problem.

// using System;
// using System.Linq.Expressions;

public static class ClotureRemover
{

#region Public Methods

public static Expression<TExpressionType> RemoveCloture<TExpressionType>(
    this Expression<TExpressionType> e)
{
    var converter = new RemoveClotureVisitor();
    var newBody = converter.Visit(e.Body);
    return Expression.Lambda<TExpressionType>(newBody, e.Parameters);
}

#endregion

private class RemoveClotureVisitor : ExpressionVisitor
{


    public RemoveClotureVisitor()
    {
    }


    public override Expression Visit(Expression node)
    {
        if (!RequiresParameterVisitor.RequiresParameter(node))
        {
            Expression<Func<object>> funct = () => new object();
            funct = Expression.Lambda<Func<object>>(Expression.Convert(node, typeof(object)), funct.Parameters);
            object res = funct.Compile()();
            return ConstantExpression.Constant(res, node.Type);
        }
        return base.Visit(node);
    }


    protected override Expression VisitBinary(BinaryExpression node)
    {
        if ((node.NodeType == ExpressionType.AndAlso) || (node.NodeType == ExpressionType.OrElse))
        {
            Expression newLeft = Visit(node.Left);
            Expression newRight = Visit(node.Right);

            bool isOr = (node.NodeType == ExpressionType.OrElse);
            bool value;
            if (IsBoolConst(newLeft, out value))
            {
                if (value ^ isOr)
                {
                    return newRight;
                }
                else
                {
                    return newLeft;
                }
            }

            if (IsBoolConst(newRight, out value))
            {
                if (value ^ isOr)
                {
                    return newLeft;
                }
                else
                {
                    return newRight;
                }
            }
        }
        return base.VisitBinary(node);
    }

    protected override Expression VisitUnary(UnaryExpression node)
    {
        if (node.NodeType == ExpressionType.Convert || node.NodeType == ExpressionType.ConvertChecked)
        {
            Expression newOpperand = Visit(node.Operand);
            if (newOpperand.Type == node.Type)
            {
                return newOpperand;
            }
        }
        return base.VisitUnary(node);
    }

    private static bool IsBoolConst(Expression node, out bool value)
    {
        ConstantExpression asConst = node as ConstantExpression;
        if (asConst != null)
        {
            if (asConst.Type == typeof(bool))
            {
                value = (bool)asConst.Value;
                return true;
            }
        }
        value = false;
        return false;
    }
}

private class RequiresParameterVisitor : ExpressionVisitor
{
    protected RequiresParameterVisitor()
    {
        result = false;
    }

    public static bool RequiresParameter(Expression node)
    {
        RequiresParameterVisitor visitor = new RequiresParameterVisitor();
        visitor.Visit(node);
        return visitor.result;
    }

    protected override Expression VisitParameter(ParameterExpression node)
    {
        result = true;
        return base.VisitParameter(node);
    }

    internal bool result;
}

}

两仪 2024-10-19 16:59:20

首先,我会通过首先避免它来解决你的问题。我对此有不同的方法。

public IEnumerable<Product> GetProductsByName(string name)
public IEnumerable<Product> GetProudctsByNameAndStock(string name, int stock)
public IEnumerable<Product> GetProductsByNameAndReserved(
    string name,
    int reserved
)
public IEnumerable<Product> GetProducts(string name, int stock, int reserved)

这些都可以通过 lambda 表达式轻松实现。例如:

public IEnumerable<Product> GetProductsByName(string name) {
    return GetProductsByExpression(p => p.Name == name);
}

private IEnumerable<Product> GetProductsByExpression(
    Expression<Func<Product, bool>> expression
) {
    return _repository.FindAll(expression);
}

等等。

这可能吗?必要时(当我的参数有价值时)添加一些条件?

其次,是的,你想做的事情是可能的,但这不是我解决问题的方式。

First, I'd solve your problem by avoiding it in the first place. I'd have different methods for this.

public IEnumerable<Product> GetProductsByName(string name)
public IEnumerable<Product> GetProudctsByNameAndStock(string name, int stock)
public IEnumerable<Product> GetProductsByNameAndReserved(
    string name,
    int reserved
)
public IEnumerable<Product> GetProducts(string name, int stock, int reserved)

These all have trivially easy implementations in terms of a lambda expression. For example:

public IEnumerable<Product> GetProductsByName(string name) {
    return GetProductsByExpression(p => p.Name == name);
}

private IEnumerable<Product> GetProductsByExpression(
    Expression<Func<Product, bool>> expression
) {
    return _repository.FindAll(expression);
}

etc.

Is it possible ? Ta add some conditions when necessary (when my parameters has value) ?

Second, yes what you want to do is possible but it's not the way I'd solve the problem.

深府石板幽径 2024-10-19 16:59:20

您的存储库方法定义建议您将 FindAll 视为传递条件并返回完整结果的内容。为什么不直接让结果为 IQueryable 类型并返回 Session.QueryOver?

然后,您的服务层将执行类似的操作,将“wheres”链接在一起:


var query = _repository.FindAll();
if (!string.IsNullOrEmpty(name))
  query = query.Where(x => x.Name == name);
if (stock.HasValue)
  query = query.Where(x => x.Stock == stock);
etc...

返回查询.ToList();

Your repository method definition suggests that you see FindAll as something that you pass criteria in and get a completed result back. Why not instead just have the result be of type IQueryable and return Session.QueryOver?

Your service layer would then do something like this, chaining together the "wheres":


var query = _repository.FindAll();
if (!string.IsNullOrEmpty(name))
  query = query.Where(x => x.Name == name);
if (stock.HasValue)
  query = query.Where(x => x.Stock == stock);
etc...

return query.ToList();

美煞众生 2024-10-19 16:59:20

所以这就是你如何将 lambda 结合在一起 - 它借用了 的大部分代码desco 的这个很棒的答案值得投赞成票。

public static class AddExpressions
{
   public static Expression<Func<TFrom, TTo>> AndLambdas<TFrom, TTo>(this Expression<Func<TFrom, TTo>> first, Expression<Func<TFrom, TTo>> second)
   {    
     ParameterExpression paramToUse = first.Parameters[0];
     Expression bodyLeft = first.Body;
     ConversionVisitor visitor = new ConversionVisitor(paramToUse, second.Parameters[0]);
     Expression bodyRight = visitor.Visit(second.Body);
     return Expression.Lambda<Func<TFrom, TTo>>(Expression.MakeBinary(ExpressionType.AndAlso, bodyLeft, bodyRight), first.Parameters);
   }

class ConversionVisitor : ExpressionVisitor
{
    private readonly ParameterExpression newParameter;
    private readonly ParameterExpression oldParameter;

    public ConversionVisitor(ParameterExpression newParameter, ParameterExpression oldParameter)
    {
        this.newParameter = newParameter;
        this.oldParameter = oldParameter;
    }

    protected override Expression VisitParameter(ParameterExpression node)
    {
        return newParameter; // replace all old param references with new ones
    }

    protected override Expression VisitMember(MemberExpression node)
    {
        if (node.Expression != oldParameter) // if instance is not old parameter - do nothing
            return base.VisitMember(node);

        var newObj = Visit(node.Expression);
        var newMember = newParameter.Type.GetMember(node.Member.Name).First();
        return Expression.MakeMemberAccess(newObj, newMember);
    }
}

然后

调用代码就很简单了

    class MyClass
    {
        public string Name { get; set; }
        public bool Hero { get; set; }
        public int Age { get; set; }

    }

......

 Expression<Func<MyClass, bool>> expression1 = x => x.Age > (age ?? 0);
 Expression<Func<MyClass, bool>> expression2 = x => x.Name == name;

 expression1 = expression1.AndLambdas(expression2);
 result = expression1.Compile()(new MyClass { 
            Name = "Rondon", 
            Hero = true, 
            Age = 92 });

So here is how you could actually and lambdas together - it borrows most of it's code from this awesome answer from desco that deserves an up-vote.

public static class AddExpressions
{
   public static Expression<Func<TFrom, TTo>> AndLambdas<TFrom, TTo>(this Expression<Func<TFrom, TTo>> first, Expression<Func<TFrom, TTo>> second)
   {    
     ParameterExpression paramToUse = first.Parameters[0];
     Expression bodyLeft = first.Body;
     ConversionVisitor visitor = new ConversionVisitor(paramToUse, second.Parameters[0]);
     Expression bodyRight = visitor.Visit(second.Body);
     return Expression.Lambda<Func<TFrom, TTo>>(Expression.MakeBinary(ExpressionType.AndAlso, bodyLeft, bodyRight), first.Parameters);
   }

class ConversionVisitor : ExpressionVisitor
{
    private readonly ParameterExpression newParameter;
    private readonly ParameterExpression oldParameter;

    public ConversionVisitor(ParameterExpression newParameter, ParameterExpression oldParameter)
    {
        this.newParameter = newParameter;
        this.oldParameter = oldParameter;
    }

    protected override Expression VisitParameter(ParameterExpression node)
    {
        return newParameter; // replace all old param references with new ones
    }

    protected override Expression VisitMember(MemberExpression node)
    {
        if (node.Expression != oldParameter) // if instance is not old parameter - do nothing
            return base.VisitMember(node);

        var newObj = Visit(node.Expression);
        var newMember = newParameter.Type.GetMember(node.Member.Name).First();
        return Expression.MakeMemberAccess(newObj, newMember);
    }
}

}

Then calling the code is quite simple ....

    class MyClass
    {
        public string Name { get; set; }
        public bool Hero { get; set; }
        public int Age { get; set; }

    }

...

 Expression<Func<MyClass, bool>> expression1 = x => x.Age > (age ?? 0);
 Expression<Func<MyClass, bool>> expression2 = x => x.Name == name;

 expression1 = expression1.AndLambdas(expression2);
 result = expression1.Compile()(new MyClass { 
            Name = "Rondon", 
            Hero = true, 
            Age = 92 });
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文