Expression parsing - Possible to get array of property names as string? - c#

Is it possible to complete this method? Is it possible in the latest version of C#? Thinking about this as a DSL to configure a system for watching for certain property changes on certain objects.
List<string> list = GetProps<AccountOwner>(x => new object[] {x.AccountOwnerName, x.AccountOwnerNumber});
// would return "AccountOwnerName" and "AccountOwnerNumber"
public List<string> GetProps<T>(Expression<Func<T, object[]>> exp)
{
// code here
}

In C# 6, you'd use:
List<string> list = new List<string>
{
nameof(AccountOwner.AccountOwnerName),
nameof(AccountOwner.AccountOwnerNumber)
};
Before that, you could certainly break the expression tree apart - the easiest way of working out how is probably to either use an expression tree visualizer, or use the code you've got and put a break point in the method (just make it return null for now) and examine the expression tree in the debugger. I'm sure it won't be very complicated - just a bit more than normal due to the array.
You could possibly simplify it using an anonymous type, if you use:
List<string> list = Properties<AccountOwner>.GetNames(x => new {x.AccountOwnerName, x.AccountOwnerNumber});
Then you could have:
public static class Properties<TSource>
{
public static List<string> GetNames<TResult>(Func<TSource, TResult> ignored)
{
// Use normal reflection to get the properties
}
}
If you don't care about the ordering, you could just use
return typeof(TResult).GetProperties().Select(p => p.Name).ToList();
If you do care about the ordering, you'd need to look at the names the C# compiler gives to the constructor parameters instead - it's a bit ugly. Note that we don't need an expression tree though - we only need the property names from the anonymous type. (An expression tree would work just as well, admittedly.)

Without c# 6 and nameof, you could get a property name from a expression tree like:
using System.Linq.Expressions;
//...
static string GetNameOf<T>(Expression<Func<T>> property)
{
return (property.Body as MemberExpression).Member.Name;
}
Using it like:
GetNameOf(() => myObject.Property);
Not directly usable for an array of objects, but you could make an overload to take an array of expressions... something like:
static string[] GetNameOf(IEnumerable<Expression<Func<object>>> properties)
{
return properties.Select(GetNameOf).ToArray();
}
And use it like
GetNameOf(
new Expression<Func<object>>[]
{
() => x.AccountOwnerName,
() => x.AccountOwnerNumber
}
);
Demonstrating fiddle: https://dotnetfiddle.net/GsV96t
Update
If you go this route, the original GetNameOf for a single property won't work for value types (since they get boxed to object in the Expression and now the expression uses Convert internally). This is easily solvable by changing the code to something like:
static string GetNameOf<T>(Expression<Func<T>> property)
{
var unary = property.Body as UnaryExpression;
if (unary != null)
return (unary.Operand as MemberExpression).Member.Name;
return (property.Body as MemberExpression).Member.Name;
}
Updated fiddle: https://dotnetfiddle.net/ToXRuu
Note: in this updated fiddle I've also updated the overloaded method to return a List instead of an array, since that's what was on your original code

Related

How can I dynamically select properties for equivalency test - FluentAssertions

I'm creating unit tests in which I will be comparing lists of objects with one another.
Currently I am using Fluent assertions in combination with specflow and nunit. I already use the Fluent Assertions to make a comparison as following:
public void TestShizzle()
{
// I normally retrieve these lists from a moq database or a specflow table
var expected = list<myObject>
{
new myObject
{
A = 1,
B = "abc"
}
}
var found = list<myObject>
{
new myObject
{
A = 1,
B = "def"
}
}
// this comparison only compares a few columns. The comparison is also object dependent. I would like to make this dynamic
found.Should().BeEquivalentTo(
expected,
options =>
options.Including(x => x.A));
}
What I really want is to be able to use generics instead of a specified type. I also want to decide which properties to compare at compile time. This is because of the large number of tables in the database. I think i need to use Linq Expressions for this, but I don't know how to go about this. The function should look something like this:
public void GenericShizzle<T>(List<T> expected, List<T> found, IEnumerable<PropertyInfo> properties)
{
Expression<Func<T, object>> principal;
foreach(var property in properties)
{
// create the expression for including fields
}
found.Should().BeEquivalentTo(
expected,
options =>
// here is need to apply the expression.
}
I have no real idea how to get the correct expression for the job, or if this even the best method. I think I need to create an property expression that is understood by the include function, but maybe a different method can be used?
There is Including method overload accepting Expression<Func<IMemberInfo, bool>> which can be used to dynamically filter members based on information about them:
IEnumerable<PropertyInfo> properties = ...;
var names = properties
.Select(info => info.Name)
.ToHashSet();
found.Should()
.BeEquivalentTo(expected,
options => options.Including((IMemberInfo mi) => names.Contains(mi.Name))); // or just .Including(mi => names.Contains(mi.Name))

How to cast an `IEnumerable<Unknown T>` to `IEnumerable<Whatever>`

I am working on a project to filter a list in a generic way. I am retrieving an IEnumerable<T> at runtime but I don't know what is T. I need to cast the list I am retrieving to IEnumerable<T> and not IEnumerable, because I need extension methods like ToList and Where. Here is my code.
private IList<object> UpdateList(KeyValuePair<string, string>[] conditions)
{
// Here I want to get the list property to update
// The List is of List<Model1>, but of course I don't know that at runtime
// casting it to IEnumerable<object> would give Invalid Cast Exception
var listToModify = (IEnumerable<object>)propertyListInfoToUpdate.GetValue(model);
foreach (var condition in conditions)
{
// Filter is an extension method defined below
listToModify = listToModify.Filter(condition .Key, condition .Value);
}
// ToList can only be invoked on IEnumerable<T>
return listToModify.ToList();
}
public static IEnumerable<T> Filter<T>(this IEnumerable<T> source, string propertyName, object value)
{
var parameter = Expression.Parameter(typeof(T), "x");
var property = Expression.Property(parameter, propertyName);
var propertyType = ((PropertyInfo)property.Member).PropertyType;
Expression constant = Expression.Constant(value);
if (((ConstantExpression)constant).Type != propertyType)
{ constant = Expression.Convert(constant, propertyType); }
var equality = Expression.Equal(property, constant);
var predicate = Expression.Lambda<Func<T, bool>>(equality, parameter);
var compiled = predicate.Compile();
// Where can only be invoked on IEnumerable<T>
return source.Where(compiled);
}
Also please note that I cannot retrieve the List like this
((IEnumerable)propertyListInfoToUpdate.GetValue(model)).Cast<object>()
since it will generate the below exception in the Filter extention
ParameterExpression of type 'Model1' cannot be used for delegate parameter of type 'System.Object'
Use GetGenericArguments and MakeGenericMethod to interface generic signatures.
private IList<object> UpdateList(KeyValuePair<string, string> conditions)
{
var rawList = (IEnumerable)propertyListInfoToUpdate.GetValue(model);
var listItemType = propertyListInfoToUpdate.PropertyType.GetGenericArguments().First();
var filterMethod = this.GetType().GetMethod("Filter").MakeGenericMethod(genericType);
object listToModify = rawList;
foreach (var condition in conditions)
{
listToModify = filterMethod.Invoke(null, new object[] { listToModify, condition.Key, condition.Value })
}
return ((IEnumerable)listToModify).Cast<object>().ToList();
}
Assuming your propertyListInfoToUpdate is a PropertyInfo and the property type is List<T>.
Why are you using Expression at all? It's hard to understand your question without a good Minimal, Complete, and Verifiable code example. Even if we could solve the casting question, you're still returning IList<object>. It's not like the consumer of the code will benefit from the casting.
And it's not really possible to solve the casting problem, at least not in the way you seem to want. A different approach would be to call the Filter() dynamically. In the olden days, we'd have to do this by using reflection, but the dynamic type gives us runtime support. You could get it to work something like this:
private IList<object> UpdateList(KeyValuePair<string, string>[] conditions)
{
dynamic listToModify = propertyListInfoToUpdate.GetValue(model);
foreach (var condition in conditions)
{
// Filter is an extension method defined below
listToModify = Filter(listToModify, condition.Key, condition.Value);
}
// ToList can only be invoked on IEnumerable<T>
return ((IEnumerable<object>)Enumerable.Cast<object>(listToModify)).ToList();
}
NOTE: your original code isn't valid; I made the assumption that conditions is supposed to be an array, but of course if you change it to anything that has a GetEnumerator() method, that would be fine.
All that said, it seems to me that given the lack of a compile-time type parameter, it would be more direct to just change your Filter() method so that it's not generic, and so that you use object.Equals() to compare the property value to the condition value. You seem to be jumping through a lot of hoops to use generics, without gaining any of the compile-time benefit of generics.
Note that if all this was about was executing LINQ query methods, that could be addressed easily simply by using Enumerable.Cast<object>() and using object.Equals() directly. It's the fact that you want to use expressions to access the property value (a reasonable goal) that is complicating the issue. But even there, you can stick with IEnumerable<object> and just build the object.Equals() into your expression.
Creating an expression and compiling it every time is very expensive. You should either use Reflection directly or a library like FastMember (or cache the expressions). In addition, your code uses Expression.Equal which translates into the equality operator (==), which is not a good way of comparing objects. You should be using Object.Equals.
Here is the code using FastMember:
private IList<object> UpdateList(KeyValuePair<string, string>[] conditions)
{
var listToModify = ((IEnumerable)propertyListInfoToUpdate.GetValue(model)).Cast<object>();
foreach (var condition in conditions)
{
listToModify = listToModify.Where(o => Equals(ObjectAccessor.Create(o)[condition.Key], condition.Value));
}
return listToModify.ToList();
}
Side note - your Filter method doesn't really need generics. It can be changed to accept and return an IEnumerable<object> by tweaking the expression, which also would've solved your problem.

Pass expression parameter as argument to another expression

I have a query which filters results:
public IEnumerable<FilteredViewModel> GetFilteredQuotes()
{
return _context.Context.Quotes.Select(q => new FilteredViewModel
{
Quote = q,
QuoteProductImages = q.QuoteProducts.SelectMany(qp => qp.QuoteProductImages.Where(qpi => q.User.Id == qpi.ItemOrder))
});
}
In the where clause I'm using the parameter q to match a property against a property from the parameter qpi.
Because the filter will be used in several places I'm trying to rewrite the where clause to an expression tree which would look like something like this:
public IEnumerable<FilteredViewModel> GetFilteredQuotes()
{
return _context.Context.Quotes.Select(q => new FilteredViewModel
{
Quote = q,
QuoteProductImages = q.QuoteProducts.SelectMany(qp => qp.QuoteProductImages.AsQueryable().Where(ExpressionHelper.FilterQuoteProductImagesByQuote(q)))
});
}
In this query the parameter q is used as a parameter to the function:
public static Expression<Func<QuoteProductImage, bool>> FilterQuoteProductImagesByQuote(Quote quote)
{
// Match the QuoteProductImage's ItemOrder to the Quote's Id
}
How would I implement this function? Or should I use a different approach alltogether?
If I understand correctly, you want to reuse an expression tree inside another one, and still allow the compiler to do all the magic of building the expression tree for you.
This is actually possible, and I have done it in many occasions.
The trick is to wrap your reusable part in a method call, and then before applying the query, unwrap it.
First I would change the method that gets the reusable part to be a static method returning your expression (as mr100 suggested):
public static Expression<Func<Quote,QuoteProductImage, bool>> FilterQuoteProductImagesByQuote()
{
return (q,qpi) => q.User.Id == qpi.ItemOrder;
}
Wrapping would be done with:
public static TFunc AsQuote<TFunc>(this Expression<TFunc> exp)
{
throw new InvalidOperationException("This method is not intended to be invoked, just as a marker in Expression trees!");
}
Then unwrapping would happen in:
public static Expression<TFunc> ResolveQuotes<TFunc>(this Expression<TFunc> exp)
{
var visitor = new ResolveQuoteVisitor();
return (Expression<TFunc>)visitor.Visit(exp);
}
Obviously the most interesting part happens in the visitor.
What you need to do, is find nodes that are method calls to your AsQuote method, and then replace the whole node with the body of your lambdaexpression. The lambda will be the first parameter of the method.
Your resolveQuote visitor would look like:
private class ResolveQuoteVisitor : ExpressionVisitor
{
public ResolveQuoteVisitor()
{
m_asQuoteMethod = typeof(Extensions).GetMethod("AsQuote").GetGenericMethodDefinition();
}
MethodInfo m_asQuoteMethod;
protected override Expression VisitMethodCall(MethodCallExpression node)
{
if (IsAsquoteMethodCall(node))
{
// we cant handle here parameters, so just ignore them for now
return Visit(ExtractQuotedExpression(node).Body);
}
return base.VisitMethodCall(node);
}
private bool IsAsquoteMethodCall(MethodCallExpression node)
{
return node.Method.IsGenericMethod && node.Method.GetGenericMethodDefinition() == m_asQuoteMethod;
}
private LambdaExpression ExtractQuotedExpression(MethodCallExpression node)
{
var quoteExpr = node.Arguments[0];
// you know this is a method call to a static method without parameters
// you can do the easiest: compile it, and then call:
// alternatively you could call the method with reflection
// or even cache the value to the method in a static dictionary, and take the expression from there (the fastest)
// the choice is up to you. as an example, i show you here the most generic solution (the first)
return (LambdaExpression)Expression.Lambda(quoteExpr).Compile().DynamicInvoke();
}
}
Now we are already half way through. The above is enough, if you dont have any parameters on your lambda. In your case you do, so you want to actually replace the parameters of your lambda to the ones from the original expression. For this, I use the invoke expression, where I get the parameters I want to have in the lambda.
First lets create a visitor, that will replace all parameters with the expressions that you specify.
private class MultiParamReplaceVisitor : ExpressionVisitor
{
private readonly Dictionary<ParameterExpression, Expression> m_replacements;
private readonly LambdaExpression m_expressionToVisit;
public MultiParamReplaceVisitor(Expression[] parameterValues, LambdaExpression expressionToVisit)
{
// do null check
if (parameterValues.Length != expressionToVisit.Parameters.Count)
throw new ArgumentException(string.Format("The paraneter values count ({0}) does not match the expression parameter count ({1})", parameterValues.Length, expressionToVisit.Parameters.Count));
m_replacements = expressionToVisit.Parameters
.Select((p, idx) => new { Idx = idx, Parameter = p })
.ToDictionary(x => x.Parameter, x => parameterValues[x.Idx]);
m_expressionToVisit = expressionToVisit;
}
protected override Expression VisitParameter(ParameterExpression node)
{
Expression replacement;
if (m_replacements.TryGetValue(node, out replacement))
return Visit(replacement);
return base.VisitParameter(node);
}
public Expression Replace()
{
return Visit(m_expressionToVisit.Body);
}
}
Now we can advance back to our ResolveQuoteVisitor, and hanlde invocations correctly:
protected override Expression VisitInvocation(InvocationExpression node)
{
if (node.Expression.NodeType == ExpressionType.Call && IsAsquoteMethodCall((MethodCallExpression)node.Expression))
{
var targetLambda = ExtractQuotedExpression((MethodCallExpression)node.Expression);
var replaceParamsVisitor = new MultiParamReplaceVisitor(node.Arguments.ToArray(), targetLambda);
return Visit(replaceParamsVisitor.Replace());
}
return base.VisitInvocation(node);
}
This should do all the trick.
You would use it as:
public IEnumerable<FilteredViewModel> GetFilteredQuotes()
{
Expression<Func<Quote, FilteredViewModel>> selector = q => new FilteredViewModel
{
Quote = q,
QuoteProductImages = q.QuoteProducts.SelectMany(qp => qp.QuoteProductImages.Where(qpi => ExpressionHelper.FilterQuoteProductImagesByQuote().AsQuote()(q, qpi)))
};
selector = selector.ResolveQuotes();
return _context.Context.Quotes.Select(selector);
}
Of course I think you can make here much more reusability, with defining expressions even on a higher levels.
You could even go one step further, and define a ResolveQuotes on the IQueryable, and just visit the IQueryable.Expression and creating a new IQUeryable using the original provider and the result expression, e.g:
public static IQueryable<T> ResolveQuotes<T>(this IQueryable<T> query)
{
var visitor = new ResolveQuoteVisitor();
return query.Provider.CreateQuery<T>(visitor.Visit(query.Expression));
}
This way you can inline the expression tree creation. You could even go as far, as override the default query provider for ef, and resolve quotes for every executed query, but that might go too far :P
You can also see how this would translate to actually any similar reusable expression trees.
I hope this helps :)
Disclaimer: Remember never copy paste code from anywhere to production without understanding what it does. I didn't include much error handling here, to keep the code to minimum. I also didn't check the parts that use your classes if they would compile. I also don't take any responsability for the correctness of this code, but i think the explanation should be enough, to understand what is happening, and fix it if there are any issues with it.
Also remember, that this only works for cases, when you have a method call that produces the expression. I will soon write a blog post based on this answer, that allows you to use more flexibility there too :P
Implementing this your way will cause an exception thrown by ef linq-to-sql parser. Within your linq query you invokes FilterQuoteProductImagesByQuote function - this is interpreted as Invoke expression and it simply cannot be parsed to sql. Why? Generally because from SQL there is no possibility to invoke MSIL method. The only way to pass expression to query is to store it as Expression> object outside of the query and then pass it to Where method. You can't do this as outside of the query you will not have there Quote object. This implies that generally you cannot achieve what you wanted. What you possibly can achieve is to hold somewhere whole expression from Select like this:
Expression<Func<Quote,FilteredViewModel>> selectExp =
q => new FilteredViewModel
{
Quote = q,
QuoteProductImages = q.QuoteProducts.SelectMany(qp => qp.QuoteProductImages.AsQueryable().Where(qpi => q.User.Id == qpi.ItemOrder)))
};
And then you may pass it to select as argument:
_context.Context.Quotes.Select(selectExp);
thus making it reusable. If you would like to have reusable query:
qpi => q.User.Id == qpi.ItemOrder
Then first you would have to create different method for holding it:
public static Expression<Func<Quote,QuoteProductImage, bool>> FilterQuoteProductImagesByQuote()
{
return (q,qpi) => q.User.Id == qpi.ItemOrder;
}
Application of it to your main query would be possible, however quite difficult and hard to read as it will require defining that query with use of Expression class.

Using Lambda Expression on an ObservableCollection

in my Silverlight 4 application, I have an ObservableCollection which consists of objects of a class and is defined by an interface:
interface myInterface()
{
string Name { get; set; }
string Value { get; set; }
}
class myClass() : myInterface
{
...
}
ObservableCollection<myInterface> _collection;
Before adding a new element to the collection, I want to make sure, that the Name-Property does not already exists within the current collection elements.
As I cannot work with contains, I currently iterate through all elements and check each element manually.
private bool CollectionContainsElement(string name2CheckAgainst)
{
foreach (myInterface item in _collection)
if (item.Name.Equals(name2CheckAgainst))
return true;
return false;
}
I have read that this can also be achieved via a Lambda Expression, so I wrote the following:
if (!_collection.Contains(p => p.Name == name2CheckAgainst))
{
...
But now I get an error, saying that the "lambda expression could not be converted to the Type "myInterface", because it is no delegate-type". (Wording may differ, as I translated it from the german version)
I'm not sure what I have to change to make it work. using System.Linq; is included. And the second question (or maybe the primary question): I have read, that the runtime changes from O(1) for the Contains()-method to O(n) - which isn't faster than my current check. So does it even make sense to change it to using the lambda? And finally, is there probably another method in checking for an existing Name-Property in my class?
Thanks in advance,
Frank
You don't have to write a Contains method, the Any method of Linq is already doing that:
if (!_collection.Any(p => p.Name == name2CheckAgainst))
If you want to use a Lambda, you have to change the prototype of your Contains method to accept a Lambda (a lambda is just an alternative way to write an anonymous function):
private bool CollectionContainsElement(Func<myInterface, bool> lambda)
{
foreach (myInterface item in _collection)
if (lambda(item))
return true;
return false;
}
Using a lambda here doesn't change the complexity of your function, it's O(n) in both case. So it's just a matter of preference.
You can use the Linq Any() method. Which is useable like so:
if (!_collection.Any(p => p.Name == name2CheckAgainst))
{
}
The reason why the contains method is O(1) is that under the covers it loads your collection into a HashTable (or similar) and uses the hash code (followed by a call to Equals) to check whether an element is present.
Contains is not a LINQ extension and therefore you can't use lambda expressions with it. It was designed to check if provided object exists in the list.
As others have said, Any is a equivalent lambda-expression compatible extension method

when and in what scenario to use Expression Tree

I was reading about Expression Tree feature and how you can create delegates using lambda expressions. I still can't get as to in what scenario it is useful and in what real world example should I use it.
The primary use for expression trees is for out-of-process LINQ providers such as LINQ to SQL.
When you write something like this:
var query = people.Where(x => x.Age > 18)
.Select(x => x.Name);
those lambda expressions can either be converted to delegates, which can then be executed (as they are in LINQ to Object) or they can be converted to expression trees, which can be analyzed by the query source and acted on accordingly (e.g. by turning them into SQL, web service calls etc). The difference is that expression trees represent the code as data. They can be compiled into delegates if necessary, but usually (within LINQ anyway) they're never executed directly - just examined to find out the logic they contain.
Expression trees are also used extensively in the Dynamic Language Runtime, where they represent the code which should execute when a dynamic expression is evaluated. Expression trees are well suited for this as they can be composed and broken down again, and after they're compiled the resulting IL is JIT-compiled as normal.
Most developers will never need to mess with the expression tree API, although it has a few other uses.
Aside from LINQ, another very simple use case is to extract both the name and the value of a property. I use this in a fluent API for validating data transfer objects. It's safer to pass one lambda parameter to define both name and value rather than have a second string parameter for the name, and run the risk of developers getting it wrong.
Here's an example (minus all the safety checks and other housekeeping):
public Validator<T> Check<T>(Expression<Func<T>> expr) {
// Analyse the expression as data
string name = ((MemberExpression) expr.Body).Member.Name;
// Compile and execute it to get the value
T value = (expr.Compile())();
return new Validator<T>(name, value);
}
Example of use:
Check(() => x.Name).NotNull.MinLength(1);
Check(() => x.Age).GreaterThan(18);
I used expression trees to make a null-safe evaluator:
string name = myObject.NullSafeEval(x => x.Foo.GetBar(42).Baz.Name, "Default");
This methods analyzes and rewrites the expression tree to insert null checks before each property or method call along the "path" to Name. If a null is encountered along the way, the default value is returned.
See implementation here
Expression trees are also commonly used to avoid referring to a property by hard-coding its name in a string:
private string _foo;
public string Foo
{
get { return _foo; }
set
{
_foo = value;
OnPropertyChanged(() => Foo);
// Rather than:
// OnPropertyChanged("Foo");
}
}
static string GetPropertyName<T>(Expression<Func<T>> expr)
{
var memberExpr = expr.Body as MemberExpression;
if (memberExpr == null)
throw new ArgumentException("expr", "The expression body must be a member expression");
return memberExpr.Member.Name;
}
protected void OnPropertyChanged<T>(Expression<Func<T>> expr)
{
OnPropertyChanged(GetPropertyName(expr));
}
This enables compile time checking and name refactoring

Categories

Resources