I have something like this:
public Expression<Func<Message, bool>> FilterData()
{
switch (this.operatorEnum)
{
case FilterParameterOperatorEnum.EqualTo:
return message => !string.IsNullOrEmpty(message.Body) &&
message.Body
.Equals(this.value, StringComparison.InvariantCultureIgnoreCase);
case FilterParameterOperatorEnum.NotEqualTo:
return message => !string.IsNullOrEmpty(message.Body) &&
!message.Body
.Equals(this.value, StringComparison.InvariantCultureIgnoreCase);
case FilterParameterOperatorEnum.Contains:
return message =>
!string.IsNullOrEmpty(message.Body) &&
message.Body.IndexOf(this.value,
StringComparison.InvariantCultureIgnoreCase) >= 0;
case FilterParameterOperatorEnum.DoesNotContain:
return message =>
!string.IsNullOrEmpty(message.Body) &&
message.Body.IndexOf(this.value,
StringComparison.InvariantCultureIgnoreCase) == -1;
}
}
As you can see this is done on Message.Body
I now what to do the same thing on other string properties on the Message class and I don't want to duplicate all that code.
Is there a way to do that by passing in the property somehow?
Eclude the expression that retrieves the property value into a separate lambda expression:
public Expression<Func<Message, bool>> FilterData(Func<Message, string> retrievePropValueFunc)
In your filter expressions, you can then call that new lambda expression (just showing one as an example):
return message => !string.IsNullOrEmpty(retrievePropValueFunc(message))
&& retrievePropValueFunc(message)
.Equals(this.value, StringComparison.InvariantCultureIgnoreCase);
To get to the Body property, pass message => message.Body to the retrievePropValueFunc parameter; as you see, you can modify this to pass different lambda expressions for the retrieval of other properties just as well.
You can try composing the expression completely manually, which will give you the ability to specify the property name as a string. This isn't a complete solution and it's untested, but it may give you a starting point to see what I mean:
var parameter = Expression.Parameter(typeof(Message), "o");
var getname = Expression.Property(parameter, "Body");
var isnullorempty = Expression.Not(Expression.Call(typeof(string), "IsNullOrEmpty", null, getname));
var compare = Expression.Equal(getname, Expression.Constant("thisvalue"));
var combined = Expression.And(isnullorempty, compare);
var lambda = Expression.Lambda(combined, parameter);
So you would change your function to accept "Body" as a parameter, and then cast the lambda at the end to:
expression<func<Message, bool>>
I may not have got the syntax for creating the lambda exactly right though.
Just change your function to receive the property instead of the message.
Or the hard way pass one more parameter for the property name and select it using reflection.
Edit just for one option than change for all the options.
public Func<Message, string, bool> FilterData()
{
return (message, propName) =>
{
var prop = message.GetType().GetProperty(propName);
if(prop != null){
var propValue = (string)prop.GetValue(message,null);
return !string.IsNullOrEmpty(propValue) && ...;
}
return false;
};
}
A very quick and dirty approach could be an enum plus some kind of magic:
public enum FilterTarget { Body, AnyOtherProp };
public Expression<Func<Message, bool>> FilterData(FilterTarget filterTarget)
{
string toBeFiltered = string.Empty;
switch(filterTarget)
{
case FilterTarget.Body :
{ toBeFiltered = message.Body; } break;
case FilterTarget.AnyOtherProp :
{ toBeFiltered = message.AnyOtherProp; } break;
default:
{
throw new ArgumentException(
string.Format("Unsupported property {0}", filterTarget.ToString()
);
}
}
switch (this.operatorEnum)
{
case FilterParameterOperatorEnum.EqualTo:
return message => !string.IsNullOrEmpty(toBeFiltered) &&
toBeFiltered.Equals(this.value, StringComparison.InvariantCultureIgnoreCase);
/* CUT: other cases are similar */
}
}
You can make the FilterData method even more fancy letting it accept a params FilterTarget[] thus gaining multi-filtering capabilities (off the top of my head, code not included).
Usage:
var aFilterDataResult = FilterData(FilterTarget.Body);
var anotherFilterDataResult = FilterData(FilterTarget.AnyOtherProp);
...
Related
I'm creating a simple way to get the Name and Value of Expressions in C#. However, I found a case I cannot figure out. See the code below:
public void GetValue_Object_TIn_Property_Test()
{
string val = "value";
var tuple = Tuple.Create(new object(), val);
Expression<Func<object, string>> expression = x => tuple.Item2;
Assert.AreEqual(val, expression.GetValue());
}
The .GetValue() method is my extension method.
Basically this expression-tree consists of a LambdaExpression, two MemberExpressions and a ConstantExpression, in that order.
When I try to get the name of tuple.Item2 I get memberExpression.Member.Name from the last MemberExpression. This gives me "tuple" instead of "Item2". How can I get "Item2" instead?
Also, when I try to get the value of the expression, I get the entire tuple instead of Item2. I'm using the following method to get the value:
public override bool TryGetValue(
MemberExpression memberExpression,
out T value
) {
value = default(T);
bool success = false;
var fieldInfo = memberExpression.Member as FieldInfo;
if (success = (fieldInfo != null))
{
var valueObj = fieldInfo.GetValue(expression.Value);
if (success = (valueObj is T || valueObj == null))
{
value = (T)valueObj;
}
}
return success;
}
Where the MemberExpression again is the last MemberExpression. What am I doing wrong here? What is the exact case I am missing?
Thank you in advance
Actually, the tree is a LambdaExpression whose Body is a PropertyExpression that has a Member field with a Name of "Item2" and an Expression that is aFieldExpression for getting the value of tuple. Note that PropertyExpression and FieldExpression are internal types that inherit from MemberExpression.
So you need to get the (Body as MemberExpression).Member.Name instead of the Body.Expression.Member.Name.
Think of the tree as MemberExpression(get Member:Item2 from Expression:MemberExpression(get Member:tuple from Expression:[surrounding class])).
Have you used LINQPad? It's Dump() command can show you this and more.
I'm following this SO answer to convert lambda expressions to partial SQL syntax.
However, I have problems parsing the expression for Contains. I added a method:
private bool ParseContainsExpression(MethodCallExpression expression)
{
MemberExpression member = (MemberExpression)expression.Arguments[0];
var methodInfo = typeof(List<int>).GetMethod("Contains", new Type[] { typeof(int) });
//TODO check if list contains value
return false;
}
As I'm totally new to expressions, I don't know from where get the property name and value, and the list that contains the values I want to check against. Where are these properties and values stored within the expression?
Your implementation is quite different from the example answer. You really need to inherit from ExpressionVisitor so that you can properly parse the tree.
Let's take this expression for an example:
var myList = new List<string> { "A" };
Expression<Func<string, bool>> a = (s) => myList.Contains(s);
ParseContainsExpression(a.Body as MethodCallExpression);
private bool ParseContainsExpression(MethodCallExpression expression)
{
expression.Object; //myList
expression.Arguments[0]; //s
return false;
}
Note however, these are still Expressions, they are not actual values yet. You need to invoke the expression to get the values.
However, in our case - myList is actually a ConstantExpression. So we can do this:
((expression.Object as MemberExpression).Expression as ConstantExpression).Value; //myList
Which returns us the original list. Note that it's a field access, because the expression is compiled into a closure, which puts myList as a field in the closure class. As you can see, we have to make a lot of assumptions as to the type of Expression we're handling (that it's a field access and then a constant expression). You really need a fully-fledged visitor implementation to do this properly (which the linked answer describes)
Basically you need to process the Method, Object(expression that represents the instance for instance method calls or null for static method calls) and Arguments(a collection of expressions that represent arguments of the called method) properties of the MethodCallExpression class.
Specifically for Contains, you need to avoid (or process differently if needed) the string.Contains method, and also handle static methods like Enumerable.Contains as well as instance methods like ICollection<T>.Contains, List<T>.Contains etc. In order to get the list values (when possible), you have to find some sort of a constant expression.
Here is a sample:
private bool ParseContainsExpression(MethodCallExpression expression)
{
// The method must be called Contains and must return bool
if (expression.Method.Name != "Contains" || expression.Method.ReturnType != typeof(bool)) return false;
var list = expression.Object;
Expression operand;
if (list == null)
{
// Static method
// Must be Enumerable.Contains(source, item)
if (expression.Method.DeclaringType != typeof(Enumerable) || expression.Arguments.Count != 2) return false;
list = expression.Arguments[0];
operand = expression.Arguments[1];
}
else
{
// Instance method
// Exclude string.Contains
if (list.Type == typeof(string)) return false;
// Must have a single argument
if (expression.Arguments.Count != 1) return false;
operand = expression.Arguments[0];
// The list must be IEnumerable<operand.Type>
if (!typeof(IEnumerable<>).MakeGenericType(operand.Type).IsAssignableFrom(list.Type)) return false;
}
// Try getting the list items
object listValue;
if (list.NodeType == ExpressionType.Constant)
// from constant value
listValue = ((ConstantExpression)list).Value;
else
{
// from constant value property/field
var listMember = list as MemberExpression;
if (listMember == null) return false;
var listOwner = listMember.Expression as ConstantExpression;
if (listOwner == null) return false;
var listProperty = listMember.Member as PropertyInfo;
listValue = listProperty != null ? listProperty.GetValue(listOwner.Value) : ((FieldInfo)listMember.Member).GetValue(listOwner.Value);
}
var listItems = listValue as System.Collections.IEnumerable;
if (listItems == null) return false;
// Do whatever you like with listItems
return true;
}
I am working with an open-source system and have been given the requirement to sort a set of entities by relevance to a term.
The set needs be ordered with exact matches first, then the "startswith" matches, then the "contains" matches.
The lambda expression I came up with to achieve this is:
var results = products.OrderBy(p => p.Name.StartsWith(searchTerm) ? Convert.ToString((char)0) : p.Name);
At present, the pre-written system works such that it invokes an OrderBy method with an instance of a lambda expression with reflection, in order to sort the results.
To minimise the amount of time spent on this task, I would like to conform to this convention. I've tried to create an instance of a lambda expression and pass it in as a parameter to MethodInfo.Invoke() but because it's got a variable in it, it throws an exception.
The code for the sort method of this system is:
private IOrderedQueryable<ReferenceProduct> OrderProducts(IQueryable<ReferenceProduct> filteredProducts, String sortExpression, String sortDirection, out String checkedSortExpression, out String checkedSortedDescending)
{
ProductListManager.SortExpression correspondingSortExpression = this.SortExpressions.OrderByDescending(se => String.Equals(se.Code, sortExpression, StringComparison.OrdinalIgnoreCase))
.DefaultIfEmpty(new SortExpression<Guid> { Code = String.Empty, Expression = rp => rp.ProductId })
.FirstOrDefault();
checkedSortExpression = correspondingSortExpression.Code;
checkedSortedDescending = String.Equals("desc", sortDirection, StringComparison.OrdinalIgnoreCase) ? "desc" : "asc";
MethodInfo orderMethod = (checkedSortedDescending.Equals("desc", StringComparison.OrdinalIgnoreCase) ? (Queryable.OrderByDescending) : new Func<IQueryable<Object>, Expression<Func<Object, Object>>, IOrderedQueryable<Object>>(Queryable.OrderBy)).Method.GetGenericMethodDefinition().MakeGenericMethod(typeof(ReferenceProduct), correspondingSortExpression.Expression.ReturnType);
IOrderedQueryable<ReferenceProduct> orderedProducts = orderMethod.Invoke(null, new Object[] { filteredProducts, correspondingSortExpression.Expression }) as IOrderedQueryable<ReferenceProduct>;
return orderedProducts;
}
Has anyone got any ideas?
Thanks in advance.
Ryan
This is about as short as I can make it, using dynamic to do some of the obscure method resolution (an evil hack, but very worthwhile here - doing it manually adds nothing useful). Rather than rely on the undefined scope of the "test class" that has the searchTerm, I've hoisted that up to the top level - but you may need something different (hence my continuing questions in comments about the scope of searchTerm):
class ReferenceProduct {
public string Name { get; set; }
static readonly Dictionary<string, Func<string, LambdaExpression>> knownSortFactories =
new Dictionary<string, Func<string,LambdaExpression>> {
{"name", searchTerm =>(Expression<Func<ReferenceProduct, string>>)(p => p.Name.StartsWith(searchTerm) ? Convert.ToString((char)0) : p.Name) }
// ... more here
};
public static IOrderedQueryable<ReferenceProduct> OrderProducts(IQueryable<ReferenceProduct> filteredProducts, string sortExpression, string sortDirection, string queryTerm)
{
Func<string, LambdaExpression> factory;
if (!knownSortFactories.TryGetValue(sortExpression, out factory))
throw new InvalidOperationException("Unknown sort expression: " + sortExpression);
dynamic lambda = factory(queryTerm); // evil happens here ;p
switch(sortDirection)
{
case "asc":
return Queryable.OrderBy(filteredProducts, lambda);
case "desc":
return Queryable.OrderByDescending(filteredProducts, lambda);
default:
throw new InvalidOperationException("Unknown sort direction: " + sortDirection);
}
}
}
With example usage (here using LINQ-to-Objects as a facade):
static void Main()
{
var source = new[] {
new ReferenceProduct { Name = "def" },
new ReferenceProduct { Name = "fooghi" },
new ReferenceProduct { Name = "abc" }
}.AsQueryable();
var sorted = ReferenceProduct.OrderProducts(source, "name", "asc", "foo");
var arr = sorted.ToArray();
foreach(var item in arr) {
Console.WriteLine(item.Name);
}
}
which outputs:
fooghi
abc
def
What you want to achieve is possible entirely without the reflection invoke call.
Instead of
var orderedProducts = orderMethod.Invoke(null, new Object[] { filteredProducts, correspondingSortExpression.Expression }) as IOrderedQueryable<ReferenceProduct>;
just use the Query Provider:
var orderedProducts = filteresProducts.Provider.CreateQuery<ReferenceProduct>(Expression.Call(null, orderMethod, Expression.Constant(correspondingSortExpression.Expression)));
I'm trying to set an Action inside another Action, but the Action I pass to it is still null in the end. Here's a dumbed down example:
Action<Action<VerifylocalResult>> docollectprint = (vl) =>
{
vl = (vlocres) =>
{
//DoStuff
};
//This returns false, indicating the action has been set:
Console.WriteLine((vl == null).ToString());
};
//Hookups
docollectprint(vlocobj.Action_NotExists);
//This returns true, so the action has not been set:
Console.WriteLine((vlocobj.Action_NotExists==null).ToString());
I already tried passing a getter method instead of the real deal but the result is the same.. it's still null in the end.
Action<Func<Action<VerifylocalResult>>> docollectprint = (vl) =>
{
Action<VerifylocalResult> theaction = vl();
theaction = (vlocres) =>
{
//DoStuff
};
//This returns false, indicating the action has been set
Console.WriteLine((theaction == null).ToString());
};
//Hookups
docollectprint(() => { return vlocobj.Action_NotExists; });
//This returns true, so the action has not been set
Console.WriteLine((vlocobj.Action_NotExists==null).ToString());
Is there any way to do this? Also, sorry if this has been asked before, but when I searched all I found was people trying to do Action<ref string x> or something similar.
Update (solution):
Func<string, Action<VerifylocalResult>> docollectprint = (msg) =>
{
Action<VerifylocalResult> vl = (vlocres) =>
{
/*DoStuff*/
};
return vl;
};
//Hookups
vlocobj.Action_NotExists = docollectprint("x");
References are passed by value by default. This means that any changes to a reference are scoped locally only. You should be returning something rather than attempting to alter a passed in reference. From MSDN:
A variable that is captured will not be garbage-collected until the
delegate that references it goes out of scope.
Variables introduced within a lambda expression are not visible in
the outer method.
A lambda expression cannot directly capture a ref [ByRef in VB] or
out parameter from an enclosing method.
A return statement in a lambda expression does not cause the
enclosing method to return.
A lambda expression cannot contain a goto statement, break statement,
or continue statement whose target is outside the body or in the
body of a contained anonymous function.
Let's get rid of all the actions, and try the same example with a more mundane object. Your code is basically equivalent with:
Action<string> changeString = (s) =>
{
s = "result";
Console.WriteLine(s);
};
string myString = "someString"
changeString(myString); //the reference is passed by value and won't work
changeString("someString"); //What should this do???
However, you could just return the changed thing, or in this case, just return the thing, as you are not doing anything with the value it's passed, along the line of:
Func<string> getString = () => return "result";
string myString = "someString";
myString = getString(); //works
or in your case:
Func<Action<VerifylocalResult>> docollectprint = () =>
{
return (vlocres) =>
{
//DoStuff
};
};
vlocobj.Action_NotExists = docollectprint();
To solve your problem you can use another delegate. First, declare it:
delegate void RefAction<T>(ref T reference);
Then change your method to this.
RefAction<Action<string>> docollectprint = (ref Action<string> vl) =>
{
vl = vlocres =>
{
//DoStuff
};
//Action has been set
Console.WriteLine((vl == null).ToString());
};
Action<string> action = null;
docollectprint(ref action);
//Action is still set
Console.WriteLine((action == null).ToString());
This, of course, in case you don't want to use Func for whatever reason.
It seems you want a Func<Action<VerifyLocalResult>>:
Func<Action<VerifylocalResult>> docollectprint = (vl) =>
{
vl = (vlocres) =>
{
//DoStuff
};
return vl;
};
then you can do
vlocobj.Action_NotExists = docollectprint();
I need to programatically check whether a nested property/function result in a lambda expression is null or not. The problem is that the null could be in any of the nested subproperties.
Example. Function is:
public static bool HasNull<T, Y>(this T someType, Expression<Func<T, Y>> input)
{
//Determine if expression has a null property
}
Use:
person.HasNull(d=>d.addressdetails.Street)
person.HasNull(d=>d.addressdetails[1].Street)
person.HasNull(d=>d.addressdetails.FirstOrDefault().Street)
person.HasNull(d=>d.InvoiceList.FirstOrDefault().Product.Name)
In any of the examples addressdetails or street, or invoicelist or the product or the name could be null.The code will throw an exception if i try to invoke the function and some nested property is null.
Important: I don't want to use a try catch for this because that is desastrous for the debugging performance.
The reason for this approach is to quickly check for values while i don't want to forget any nulls and so cause exceptions. This is handy for reporting solutions and grids where a null on the report can just show empty and has no futher business rules.
related post: Don't stop debugger at THAT exception when it's thrown and caught
It is possible, but I'm not sure I would recommand it. Here is something that you may find usefull: it doesn't return a boolean, but instead, the leaf value of the expression if possible (no null reference).
public static class Dereferencer
{
private static readonly MethodInfo safeDereferenceMethodInfo
= typeof (Dereferencer).GetMethod("SafeDereferenceHelper", BindingFlags.NonPublic| BindingFlags.Static);
private static TMember SafeDereferenceHelper<TTarget, TMember>(TTarget target,
Func<TTarget, TMember> walker)
{
return target == null ? default(TMember) : walker(target);
}
public static TMember SafeDereference<TTarget, TMember>(this TTarget target, Expression<Func<TTarget, TMember>> expression)
{
var lambdaExpression = expression as LambdaExpression;
if (lambdaExpression == null)
return default(TMember);
var methodCalls = new Queue<MethodCallExpression>();
VisitExpression(expression.Body, methodCalls);
var callChain = methodCalls.Count == 0 ? expression.Body : CombineMethodCalls(methodCalls);
var exp = Expression.Lambda(typeof (Func<TTarget, TMember>), callChain, lambdaExpression.Parameters);
var safeEvaluator = (Func<TTarget, TMember>) exp.Compile();
return safeEvaluator(target);
}
private static Expression CombineMethodCalls(Queue<MethodCallExpression> methodCallExpressions)
{
var callChain = methodCallExpressions.Dequeue();
if (methodCallExpressions.Count == 0)
return callChain;
return Expression.Call(callChain.Method,
CombineMethodCalls(methodCallExpressions),
callChain.Arguments[1]);
}
private static MethodCallExpression GenerateSafeDereferenceCall(Type targetType,
Type memberType,
Expression target,
Func<ParameterExpression, Expression> bodyBuilder)
{
var methodInfo = safeDereferenceMethodInfo.MakeGenericMethod(targetType, memberType);
var lambdaType = typeof (Func<,>).MakeGenericType(targetType, memberType);
var lambdaParameterName = targetType.Name.ToLower();
var lambdaParameter = Expression.Parameter(targetType, lambdaParameterName);
var lambda = Expression.Lambda(lambdaType, bodyBuilder(lambdaParameter), lambdaParameter);
return Expression.Call(methodInfo, target, lambda);
}
private static void VisitExpression(Expression expression,
Queue<MethodCallExpression> methodCallsQueue)
{
switch (expression.NodeType)
{
case ExpressionType.MemberAccess:
VisitMemberExpression((MemberExpression) expression, methodCallsQueue);
break;
case ExpressionType.Call:
VisitMethodCallExpression((MethodCallExpression) expression, methodCallsQueue);
break;
}
}
private static void VisitMemberExpression(MemberExpression expression,
Queue<MethodCallExpression> methodCallsQueue)
{
var call = GenerateSafeDereferenceCall(expression.Expression.Type,
expression.Type,
expression.Expression,
p => Expression.PropertyOrField(p, expression.Member.Name));
methodCallsQueue.Enqueue(call);
VisitExpression(expression.Expression, methodCallsQueue);
}
private static void VisitMethodCallExpression(MethodCallExpression expression,
Queue<MethodCallExpression> methodCallsQueue)
{
var call = GenerateSafeDereferenceCall(expression.Object.Type,
expression.Type,
expression.Object,
p => Expression.Call(p, expression.Method, expression.Arguments));
methodCallsQueue.Enqueue(call);
VisitExpression(expression.Object, methodCallsQueue);
}
}
You can use it this way:
var street = person.SafeDereference(d=>d.addressdetails.Street);
street = person.SafeDereference(d=>d.addressdetails[1].Street);
street = person.SafeDereference(d=>d.addressdetails.FirstOrDefault().Street);
var name = person.SafeDereference(d=>d.InvoiceList.FirstOrDefault().Product.Name);
Warning : this is not fully tested, it should work with methods and properties, but probably not with extension methods inside the expression.
Edit : Ok, it can't handle extension methods for now (e.g. FirstOrDefault) but it's still possible to adjust the solution.
You would have to take the expression apart and evaluate each bit in turn, stopping when you got a null result. This wouldn't be impossible by any means, but it would be quite a lot of work.
Are you sure this is less work than just putting explicit null guards in the code?
We definately need a null-safe dereferencing operator in C#, but until then look at this question, which provides a slightly different but also neat solution to the same problem.
Any reason why you couldn't just do the following?
bool result;
result = addressdetails != null && addressdetails.Street != null;
result = addressdetails != null && addressdetails.Count > 1 && addressdetails[1].Street != null;
result = addressdetails != null && addressdetails.FirstOrDefault() != null && addressdetails.FirstOrDefault().Street != null;
result = addressdetails != null && addressdetails.FirstOrDefault() != null && addressdetails.FirstOrDefault().Product != null && addressdetails.FirstOrDefault().Product.Name != null;
I think these simple boolean expressions would be the best way to go, they work because of conditional evaluation... When anding (&&) terms together, the first false term will return false and stop the rest from being evaluated, so you wouldn't get any exceptions doing this.
Though it's not the answer on this exact question, the easiest way I know to achieve the same behaviour is to pass pathes to properties as enumerables of property names instead of call chains.
public static bool HasNull<T, Y>(this T someType, IEnumerable<string> propertyNames)
Then parse those enumerables in a circle or recursively using reflection. There are some disadvantages like loosing intelly sense and static name checking, but very easy to implement, which may overweigh them in some cases.
Instead of writing
person.HasNull(d=>d.addressdetails.FirstOrDefault().Street)
you'll have to write
person.HasNull(new string[] { "addressdetails", "0", "Street" })
I blogged about, it works in VB.NET (I translated to C#, but I didn't test the C# version, check it out).
Checkout: How would you check car.Finish.Style.Year.Model.Vendor.Contacts.FirstOrDefault().FullName for null? :-)
Dim x = person.TryGetValue(
Function(p) p.addressdetail(1).FirstOrDefault().Product.Name)