I have a method which converts a LambdaExpression to a string. I use these strings as keys for a cache.
string p = "x";
var a = LambdaToString<MyType>(m => m.P == p);
is different from this:
string p = "y";
var a = LambdaToString<MyType>(m => m.P == p);
However, the current state of my LambdaToString method is producing the same output regardless of the value of p. Which is:
(MyType.P == value(ConsoleApplication1.Program+<>c__DisplayClass0).p)
What I would like my LambdaToString function to do is to resolve the "value(class).p" portion of the expression into the actual literal string of "x" or "y" as the case may be.
Here is the current state of my LambdaToString method. I am not sure what I would need to do to modify it to produce the outputs I want:
public static string LambdaToString<T>(Expression<Func<T, bool>> expression)
{
string body = expression.Body.ToString();
foreach (var parm in expression.Parameters)
{
var parmName = parm.Name;
var parmTypeName = parm.Type.Name;
body = body.Replace(parmName + ".", parmTypeName + ".");
}
return body;
}
I use these strings as keys for a cache.
It's incorrect in a lot of circumstances, even it works in your project. Using Expression.ToString()
as keys can be defeated easily by:
//counter-example 1
Expression<Func<string, bool>> exp1 = s => s == "a";
Expression<Func<string, bool>> exp2 = ss => ss == "a";
//the two will be considered different in your cache solution
//but they are essentially the same, well that's not the worst, see next
//counter-example 2
Expression<Func<int, bool>> exp3 = i => i > 10;
Expression<Func<long, bool>> exp4 = i => i > 10;
//the two will be considered the same in your cache solution
//of course they are different, probably hences runtime exceptions
The above is not an answer at all. If you don't care about that, let's continue based on "using strings as keys".
You want to cache the expressions, but identify those look-same expressions with constants in them. Then why not build the key with expression+constant? In your example code, the keys will be:
"m => m.P == p @@SPECIAL_SEPERATOR@@ x"
"m => m.P == p @@SPECIAL_SEPERATOR@@ y"
and yes, if one constant contains values like "@@SPECIAL_SEPERATOR@@", everything is going to crash. This is not a rigorous solution from the very beginning, because you choose strings as the cache key.
If you decide to choose another cache approach, check this.
Well, to get p value, you could do (probably easier and more robust way to do this, but).
public static string LambdaToString<T>(Expression<Func<T, bool>> expression)
{
BinaryExpression binaryExpression = expression.Body as BinaryExpression;
Expression right = binaryExpression.Right;//right part of the "==" of your predicate
var objectMember = Expression.Convert(right, typeof(object));//convert to object, as we don't know what's in
var getterLambda = Expression.Lambda<Func<object>>(objectMember);
var getter = getterLambda.Compile();
var valueYouWant = getter();//here's the "x" or "y"
//...
or shorter
Expression right = (expression.Body as BinaryExpression).Right;
var valueYouWant = Expression.Lambda(right).Compile().DynamicInvoke();
CAUTION
Of course, this won't fit a lot of scenarii, it's just the basic to understand how to get a value. It won't work if your predicate is
var x = 1;
var y = 2;
var result = LambdaToString<YourType>(v => v.A== x && v.B == y)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With