score:2

Accepted answer

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

}

score:0

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();

score:0

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

score:1

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.


Related Query

More Query from same tag