Using expressions to get intellisense support in C# - c#

I'm trying use expressions in order to have strong typing over properties.
So I have this model.
public class Entity
{
public int Id { get; set; }
public string Name { get; set; }
}
And this method which should, well in the end I would like to use the properties in some way but for now I just want to return them.
public List<string> DoSomething<TEntity>(params Expression<Func<TEntity, object>>[] expressions)
{
List<string> props = new List<string>();
foreach (var expression in expressions)
{
var memberExpression = expression.Body as MemberExpression;
var q = memberExpression.Member.Name;
props.Add(q);
}
return props;
}
This is the usage
var props = DoSomething<Entity>(x => x.Id, x => x.Name);
Well, It works but only partially. What I mean by that is that it will work for reference types, for example it will work for Name property because it is a reference type, but it will return null for any value type, in this case for ID which is an int.
Why is that and what is the solution?

Expression<Func<TEntity, object>> is an Expression that builds a Func which returns an object (which is a reference type).
If you pass x => x.Id as value of that Expression, the return type of the resulting Func will not match, as int is a value type but the Func is expected to return a reference type.
C#'s compiler will see that and automatically build a Func that wraps the int inside an object (called boxing). But that Func no longer has a simple MemberExpression as its body, because the MemberExpression has to be wrapped in a "boxing expression".
That's basically what happens. So you have to handle the case where expression.Body is not of type MemberExpression to fix that.
This is untested, but it might help:
if (!(expression.Body is MemberExpression))
{
if (expression.Body is UnaryExpression unaryExpression && unaryExpression.NodeType == ExpressionType.TypeAs)
expression = unaryExpression.Operand as MemberExpression;
else
expression = null;
if (expression == null)
throw new ArgumentException("something happened");
}

Related

Get propertyinfo from lambda expression, but fails with int

What is the best way to get to all the PropertyInfo of items in a lambda expression.
i want to set a filter on a xml field in a sql database.
var FilterBase = new FilterBase<SimpleItemSubObject>()
.SetSimpleFilter(x => x.ID, 123)
.SetSimpleFilter(x => x.Test.Name, "demo3");
in de analyser, i'm able to get the propertyinfo for the Name property.
internal IEnumerable<PropertyInfo> GetExpressionList()
{
return GetPropertyListfor(lambda.Body as MemberExpression);
}
private IEnumerable<PropertyInfo> GetPropertyListfor(MemberExpression body)
{
var result = new List<PropertyInfo>();
if (body != null && body.Expression != null)
{
result.AddRange(GetPropertyListfor(body.Expression as MemberExpression));
result.Add((body as MemberExpression).Member as PropertyInfo);
}
return result;
}
this will return the propertyinfo if it's a string property. but in case of a int the analyser fails because lambda has added a convert function.
{x => Convert(x.ID)}
It added a convert function.
so what is the best method of getting the propertyinfo in this case for the x.ID. and how do i prevent the use of the convert function
The fact that compiler adds Convert expression indicates that you are using non generic lambda expression with object return type. Something like this:
public class FilterBase<T>
{
public FilterBase<T> SetSimpleFilter(Expression<Func<T, object>> selector, object value)
{
// ...
return this;
}
}
One way to resolve the issue is to make the method generic (similar to LINQ OrderBy):
public FilterBase<T> SetSimpleFilter<V>(Expression<Func<T, V>> selector, V value)
so there will not be Convert anymore.
Another way is to keep the method as is, and strip the first Convert if any:
internal IEnumerable<PropertyInfo> GetExpressionList()
{
var body = lambda.Body;
if (body.NodeType == ExpressionType.Convert)
body = ((UnaryExpression)body).Operand;
return GetPropertyListfor(body as MemberExpression);
}

MemberExpression as object of its type

I'm building Linq Extension methods.
Shortly, I've built an extension method in order to create a MemberExpression looks like:
public static Expression Field<T>(this object entity, string field)
{
Type entityType = entity.GetType();
PropertyInfo propertyInfo = entityType.GetProperty(field);
if (propertyInfo == null)
throw new ArgumentException(string.Format("{0} doesn't exist on {1}", field, entityType.Name));
ParameterExpression parameterExpression = Expression.Parameter(entityType, "e");
return Expression.Property(parameterExpression, propertyInfo);
}
So, I'm able to do that:
IEnumerable<C> classes = this.backend.cs.Where(
c => c.Field<C>("Matter").EndsWith(string.Empty)<<<<<<<< Compilation error.
);
Since MemberExpression have not EndsWith method, I'm not able to extend this MemberExpression like a String property access like:
IEnumerable<C> classes = this.backend.cs.Where(
c => c.Matter.EndsWith(string.Empty)
);
Is there some way to do that.
As you are able to figure out I'm trying to get something a bit more complex, Nevertheless, this example is for explaining the situation.
I hope it's enought clear.
Scope
My UI is using a backend.
This backend have three implementations. Each one of them provides a Linq implementation (Linq collections, NHibernate, custom-made Linq provider).
So, my UI is able to work on collections, a database or getting data from our server.
I'd like to provide util extension methods like AnyField().
So, after digging a bit I'm thinking on two approaches:
AnyField() generates an expression tree which is able to be translated by every Linq provider (first answer of this post).
Provide a default implementation of Anyfield() for Linq Collections, and then use each Linq provider extension mechanism for handle it. Or, if you are building a Linq Provider, support it on implementation.
Okay, so you're getting tripped up on the syntactic sugar that C# provides for you when building ExpressionTrees
Where expects Expression<Func<TObjectType, TReturnType>> or a compiled lambda; Func<TObjectType, TReturnType>.
Your method Field currently only returns an untyped Expression. That means your query is actually returning Expression<Func<TObjectType, Expression>>. That's not right! It should be returning a Expression<Func<TObjectType, string>>! But how do we do that? That would mean our method would have to return a string, but we want to build an expression tree.
To get it working as you're expecting, it's quite a bit more difficult than you would imagine, but that's only because we're so spoiled with the syntactic sugar.
What we actually need to do is write methods which accept lambda methods, and return lambda methods, each one re-writing the body a little bit.
So... what does that look like?
public static Expression<Func<TElementType, object>> Field<TElementType, TReturnType>(this Expression<Func<TElementType, TReturnType>> expr, string field)
{
Type entityType = expr.Body.Type;
PropertyInfo propertyInfo = entityType.GetProperty(field);
if (propertyInfo == null)
throw new ArgumentException(string.Format("{0} doesn't exist on {1}", field, entityType.Name));
ParameterExpression parameterExpression = Expression.Parameter(entityType, "e");
return Expression.Lambda<Func<TElementType, object>>(
Expression.Property(parameterExpression, propertyInfo),
parameterExpression
);
}
Notice that it's almost the exact same as what you wrote, but we wrap it with Lambda<Func<TElementType, TReturnType>>. And the signature is a bit different too.
Instead of operating on an object, we want to operate on a lambda expression. We also return a lambda expression.
So how do we use it?
var classes = objects.Where(
ExpressionExtensions.Field<Test, Test>(q => q, "Matter")
);
Great! Now we're passing Expression<Func<Test, string>> to Where, rather than Expression<Func<Test, MemberExpression>>. Making progress.
But that won't compile, and rightly so. We're returning a string, but we're using a filtering method, which requires a bool.
So let's now write EndsWith:
public static Expression<Func<T, bool>> EndsWith<T, TReturnType>(this Expression<Func<T, TReturnType>> expr, string str)
{
var endsWithMethod = typeof(string).GetMethod("EndsWith", new[] { typeof(string) });
var newBody = Expression.Call(expr.Body, endsWithMethod, Expression.Constant(str));
var result = Expression.Lambda<Func<T, bool>>(newBody, expr.Parameters);
return result;
}
And using it:
var classes = objects.Where(
ExpressionExtensions.Field<Test, Test>(q => q, "Matter")
.EndsWith("A")
);
Which is now compiling! And the expression tree looks like this:
UserQuery+Test[].Where(e => e.Matter.EndsWith("A"))
That's not too pretty, having Field take a redundant lambda, though. Let's add a helper method to make it look prettier:
public static Expression<Func<TElementType, TElementType>> Query<TElementType>(this Expression<Func<TElementType, TElementType>> expr)
{
return expr;
}
Putting it all together:
void Main()
{
var objects = new[] { new Test { Matter = "A" } }.AsQueryable();
var classes = objects.Where(
ExpressionExtensions.Query<Test>(q => q)
.Field("Matter")
.EndsWith("A")
);
classes.Expression.Dump();
}
public class Test
{
public string Matter { get; set;}
}
public static class ExpressionExtensions
{
public static Expression<Func<TElementType, TElementType>> Query<TElementType>(this Expression<Func<TElementType, TElementType>> expr)
{
return expr;
}
public static Expression<Func<TElementType, object>> Field<TElementType, TReturnType>(this Expression<Func<TElementType, TReturnType>> expr, string field)
{
Type entityType = expr.Body.Type;
PropertyInfo propertyInfo = entityType.GetProperty(field);
if (propertyInfo == null)
throw new ArgumentException(string.Format("{0} doesn't exist on {1}", field, entityType.Name));
ParameterExpression parameterExpression = Expression.Parameter(entityType, "e");
return Expression.Lambda<Func<TElementType, object>>(
Expression.Property(parameterExpression, propertyInfo),
parameterExpression
);
}
public static Expression<Func<T, bool>> EndsWith<T, TReturnType>(this Expression<Func<T, TReturnType>> expr, string str)
{
var endsWithMethod = typeof(string).GetMethod("EndsWith", new[] { typeof(string) });
var newBody = Expression.Call(expr.Body, endsWithMethod, Expression.Constant(str));
var result = Expression.Lambda<Func<T, bool>>(newBody, expr.Parameters);
return result;
}
}
I don't know if you mess the code to show a piece of code or if it was intended, but you have a generic extension that wants A T but you don't use it
Anyway if what you want is a method that returns you the value of a property, why don't you do a static exception that return T ?
public static class EntityExtension {
public static T Field<T>(this object entity, string field) {
Type entityType = entity.GetType();
PropertyInfo propertyInfo = entityType.GetProperty(field);
if (propertyInfo == null) {
throw new ArgumentException(string.Format("{0} doesn't exist on {1}", field, entityType.Name));
}
return (T)propertyInfo.GetValue(entity);
}
}
this is a fiddle i've done to show you the usage, pretty simple
https://dotnetfiddle.net/PoSfli
posting the code too in case fiddle get lost:
using System;
using System.Reflection;
using System.Linq.Expressions;
public class Program
{
public static void Main()
{
YourClass c = new YourClass() {
PropA = 1,
PropB = 2,
PropC = "ciao"
};
var propBValue = c.Field<int>("PropB");
Console.WriteLine("PropB value: {0}", propBValue);
var propCValue = c.Field<string>("PropC");
Console.WriteLine("PropC value: {0}", propCValue);
}
}
public static class EntityExtension {
public static T Field<T>(this object entity, string field) {
Type entityType = entity.GetType();
PropertyInfo propertyInfo = entityType.GetProperty(field);
if (propertyInfo == null) {
throw new ArgumentException(string.Format("{0} doesn't exist on {1}", field, entityType.Name));
}
return (T)propertyInfo.GetValue(entity);
}
}
public class YourClass {
public int PropA { get; set; }
public int PropB { get; set; }
public string PropC { get; set; }
}
nota that you can improve a lot, using a typed extension and a property expression as argument instead of a string
You can also do something really simple like if you want to use the property name:
IEnumerable<C> classes = this.backend.cs.Where(
c => c.Field<C>("Matter").ToString().EndsWith(string.Empty)
Or if your are filtering by property type:
IEnumerable<C> classes = this.backend.cs.Where(
c => c.Field<C>("Matter").Type.ToString().EndsWith(string.Empty)
How about something like this? Actually, your generic approach is not of use right now.
public static bool Evaluate<TField>(this object entity, string fieldName, Predicate<TField> condition)
{
Type entityType = entity.GetType();
PropertyInfo propertyInfo = entityType.GetProperty(field);
if (propertyInfo == null)
throw new ArgumentException(string.Format("{0} doesn't exist on {1}", field, entityType.Name));
var value = (TField)propertyInfo.GetValue(entity); //read the value and cast it to designated type, will raise invalid cast exception, if wrong
return condition.Invoke(value); //invoke the predicate to check the condition
}
Usage would be then.
.Where(item => item.Evaluate<string>("Matter", prop => prop.EndsWith(string.Empty))
You can add a new extension method which returns your desired type.
public static T Compile<T>(this Expression expression)
{
return Expression.Lambda<Func<T>>(expression).Compile()();
}
In your statement you just have to add .Compile<type>()
IEnumerable<C> classes = this.backend.cs.Where(
c => c.Field<C>("Matter").Compile<string>().EndsWith(string.Empty));

Create safe deep property accessor from Lambda with Expressions

My goal is to use Lambdas to create a property binding object that can do a safe retrieval of a deep property value. By safe, it returns the default value of the property type if one of previous properties is null rather than throwing a null reference exception.
The method signature:
public static Func<TO, TP> BuildSafeAccessor<TO, TP>(this Expression<Func<TO, TP>> propertyExpression) where TO: class
{
}
*Edit: Clarify my question
So if I call:
var safeAccessor = BuildSafeAccessor<Person>(p => p.Address.Zip);
When safeAccessor is called, it's logic would be the following:
if (p.Address == null)
return default(TP);
return p.Address.Zip;
The key observation here is that you don't need “the ifFalse expression fully created already”, you can build it recursively.
The code could look like this:
public static Func<TO, TP> BuildSafeAccessor<TO, TP>(Expression<Func<TO, TP>> propertyExpression)
{
var properties = GetProperties(propertyExpression.Body);
var parameter = propertyExpression.Parameters.Single();
var nullExpression = Expression.Constant(default(TP), typeof(TP));
var lambdaBody = BuildSafeAccessorExpression(parameter, properties, nullExpression);
var lambda = Expression.Lambda<Func<TO, TP>>(lambdaBody, parameter);
return lambda.Compile();
}
private static Expression BuildSafeAccessorExpression(Expression init, IEnumerable<PropertyInfo> properties, Expression nullExpression)
{
if (!properties.Any())
return init;
var propertyAccess = Expression.Property(init, properties.First());
var nextStep = BuildSafeAccessorExpression(propertyAccess, properties.Skip(1), nullExpression);
return Expression.Condition(
Expression.ReferenceEqual(init, Expression.Constant(null)), nullExpression, nextStep);
}
private static IEnumerable<PropertyInfo> GetProperties(Expression expression)
{
var results = new List<PropertyInfo>();
while (expression is MemberExpression)
{
var memberExpression = (MemberExpression)expression;
results.Add((PropertyInfo)memberExpression.Member);
expression = memberExpression.Expression;
}
if (!(expression is ParameterExpression))
throw new ArgumentException();
results.Reverse();
return results;
}
(Note that this code uses LINQ inefficiently, to make it more readable.)
If you run it on the lambda a => a.B.C.D, it will create an expression (showing the result of ToString() on it):
a => IIF((a == null), null, IIF((a.B == null), null, IIF((a.B.C == null), null, a.B.C.D)))
Notice that this accesses a.B up to three times. If that property is slow or has side effects, that could be a problem. If this is a problem to you, I think you would need to use Blocks with local variables.

How can I get my generic method to work?

I am writing a small extension to my WPF viewModel base class that allows me to define dependencies between properties in a cleaner way than the standard method of raising multiple PropertyChanged events in the getter:
So instead of:
public int ThisProperty
{
get
{
thisProperty = value;
RaisePropertyChangedEvent("ThisProperty");
RaisePropertyChangedEvent("FirstDependentProperty");
RaisePropertyChangedEvent("SecondDependentProperty");
}
}
I want to be able to do something like this in my ViewModels constructor:
RegisterDependencies("This Property",
"FirstDependentProperty", "SecondDependentProperty");
I have defined the following method in my viewModel (error checking removed to reduce the amount of code):
public void RegisterDependencies(string property, params string[] dependencies)
{
foreach (string item in dependencies)
{
IList<string> deps;
if (dependenciesList.TryGetValue(item, out deps))
{
if (!deps.Contains(property))
{
deps.Add(property);
}
}
else
{
deps = new List<string>();
deps.Add(property);
dependenciesList[item] = deps;
}
}
}
My viewmodel subscribes to the PropertyChanged event with the following method:
void ViewModel_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
IList<string> dependencies;
if (dependenciesList.TryGetValue(e.PropertyName, out dependencies))
{
foreach (string item in dependencies)
{
RaisePropertyChangedEvent(item);
}
}
}
This simply checks if the property has any dependencies, and if so, raises the PropertyChanged event for those as well. This all works beautifully, so far so good.
What I really want though is to use lambda expressions rather than strings so that I get auto-complete and better re-factoring support. What I'm after is something like this:
RegisterDependencies<ViewModelType>(p => p.Property,
p => p.FirstDependency,
p => p.SecondDependency); //and so on...
I have redefined the method signature to look like this:
public void RegisterDependencies<T>(Expression<Func<T, object>> property,
prams Expression<Func<T, object>>[] dependencies)
{
}
At the moment the method simply attempts to convert the expressions to strings and this is where I'm coming unstuck. I'm not sure how to get the name of the property from the expression and convert it to a string.
Josh Smith's MVVM Foundation contains a class called PropertyChangedObserver which has a piece of code that does this which I have attempted to adapt to suit my example:
private static string GetPropertyName<T>(Expression<Func<T, object>> expression)
{
var lambda = expression as LambdaExpression;
MemberExpression memberExpression;
if (lambda.Body is UnaryExpression)
{
var unaryExpression = lambda.Body as UnaryExpression;
memberExpression = unaryExpression.Operand as MemberExpression;
}
else
{
memberExpression = lambda.Body as MemberExpression;
}
return memberExpression != null ? ((PropertyInfo)memberExpression.Member).Name : null;
}
This is being called in my updated RegisterDependencies method (this is literally the entire code for the method at the moment):
string propertyName = GetPropertyName(property);
foreach (Expression<Func<T, object>> expr in dependencies)
{
string dependencyName = GetPropertyName(expr);
}
When this is run it results in a XamlParseException. This is difficult to debug as it doesn't throw up the standard exception window. Visual Studio's output window provides a bit more information and it seems the initial exception is a InvalidCastException although I'm not sure why as both expressions are of the same type.
Can anyone shed any light and help me with this?
Following up on Matti Virkkunen's comment, the Member property returns a MemberInfo object that can represent a property or a field. If that is your problem, no worries; the Name property is actually a member of the MemberInfo class, so there's no need to cast to PropertyInfo.
If something else is going on, we need more information to help; could you post the call site with the actual lambda expressions you're passing?
Seems Problem is in passing expression to RegisterDependencies.
Following (slightly adapted code) is successfully running.
public static void Test()
{
var deps = RegisterDependencies((KeyValuePair<string,string> p) => p.Key, (KeyValuePair<string,string> p) => p.Value);
foreach(var d in deps)
{
Console.WriteLine(d); // Prints Key, Then Value
}
}
public static IEnumerable<string> RegisterDependencies<T>(Expression<Func<T, object>> property, params Expression<Func<T, object>>[] dependencies)
{
var deps = new List<string>();
deps.Add(GetPropertyName(property));
foreach (var d in dependencies)
{
deps.Add(GetPropertyName(d));
}
return deps;
}
public static string GetPropertyName<T>(Expression<Func<T, object>> expression)
{
var lambda = expression as LambdaExpression;
MemberExpression memberExpression;
if (lambda.Body is UnaryExpression)
{
var unaryExpression = lambda.Body as UnaryExpression;
memberExpression = unaryExpression.Operand as MemberExpression;
}
else
{
memberExpression = lambda.Body as MemberExpression;
}
return memberExpression != null ? ((PropertyInfo)memberExpression.Member).Name : null;
}
It turns out that somehow I'd managed to type the property names in wrong e.g. where I wanted:
p => p.Quantity
I'd actually typed:
p => p.quantity
notice the lower-case q. Due to the fact I was calling this in my constructor I also had access to all my private members (so my type had a field named quantity) which is why this was not causing the compiler to complain but an InvalidCastException to be generated at Runtime as it is obviously not a property.
#Phoog & Tilak - Thank you for your help. Both of your answers helped me isolate the problem which is why I've up-voted both of your answers despite this being the 'correct' one.

How to write a lambda expression take in a collection of properties using a delimiter?

I am not sure how to create this expression:
(e => e.Prop1, e.Prop2, e.Prop3)
I want to pass an expression like this into a method so I can iterate through the collection and pull out the property names.
pseudo method:
public void ParseProperties<T>(The_Expression_being_Passed_In)
{
foreach(var member in expression.members)
{
.....
}
}
You need to accept a parameter of type
params Expression<Func<T, object>>[] expressions
So you can say
public void ParseProperties<T>(params Expression<Func<T, object>>[] expressions) {
foreach(var expression in expressions) {
expression.GetPropertyName();
}
}
You'll have to write GetPropertyName; implementations abound on StackOverflow.
You can't quite call it like you desire. You'll have to say
ParseProperties(e => e.Prop1, e => e.Prop2, e => e.Prop3);
you could try something like this, but it may blow up on you if you try to use a value type property as the expression will most likely be a cast expression.
public PropertyInfo ParseProperty<T>(Expression<Func<T,object>> expr)
{
MemberExpression member;
if(expr == null)
throw new ArgumentNullException("expr");
if((member = expr.Body as MemberExpression) == null)
throw new ArgumentException("incorrect format");
return member.Member as PropertyInfo;
}
public PropertyInfo[] ParseProperties<T>(params Expression<Func<T,object>> expressions)
{
return Array.ConvertAll(expressions,ParseProperty);
}

Categories

Resources