Create safe deep property accessor from Lambda with Expressions - c#

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.

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

How to use expressions to sort a collection based on an inherited interface property

This problem has been discussed to an extent in this question: Create Generic Expression from string property name but perhaps I'm missing the answer, or it is subtly different.
I have the following queryable extension method:
public static IQueryable<TSource> OrderByPropertyDescending<TSource>(IQueryable<TSource> source, string propertyName)
{
var sourceType = typeof (TSource);
var parameter = Expression.Parameter(sourceType, "item");
var orderByProperty = Expression.Property(parameter, propertyName);
var orderBy = Expression.Lambda(orderByProperty, new[] { parameter });
return Queryable.OrderByDescending(source, (dynamic) orderBy);
}
For the purposes of this problem let us assume that the queryable source instance has a property called 'Created' which is a type of DateTime. If we define a class that has the property 'Created' on it and then OrderByDescending then the above will work fine. e.g.
var queryable = new List<EntityClassWithCreatedProperty>().AsQueryable();
var result = queryable.OrderByPropertyDescending("Created").ToList();
If we do the same thing but with an interface such as ICreated which has the Created property on it:
public interface ICreated
{
DateTime Created { get; }
}
Then the following also works:
var queryable = new List<ICreated>().AsQueryable();
var result = queryable.OrderByPropertyDescending("Created").ToList();
If however, you have the following interface hierarchy:
public interface ITimestamped : ICreated
{
...
}
Then the following will fail:
var queryable = new List<ITimestamped>().AsQueryable();
var result = queryable.OrderByPropertyDescending("Created").ToList();
With the similar error message to that of the other question: Instance property 'Created' is not defined for type ITimestamped. I'm assuming that I need to find the property on the declaring type of interface (which I can do by crawling the source type) but then what do I do with it? Most attempts I have tried result in the incorrect original source type not being cast-able back to the IQueryable. Do I need to use a ConvertType call somewhere? Thanks.
I would use the other Expression.Property() method that takes a propertyInfo, e.g
public static IQueryable<TSource> OrderByPropertyDescending<TSource>(IQueryable<TSource> source, string propertyName)
{
var sourceType = typeof (TSource);
var parameter = Expression.Parameter(sourceType, "item");
var propertyInfo = FindMyProperty(sourceType, propertyName);
var orderByProperty = Expression.Property(parameter, propertyInfo);
var orderBy = Expression.Lambda(orderByProperty, new[] { parameter });
return Queryable.OrderByDescending(source, (dynamic) orderBy);
}
private static PropertyInfo FindMyProperty(Type type, string propertyName)
{
return type.GetProperty(propertyName);
}
At this point your question has nothing to do with expressions, it's a simple reflection question, and you have to find the correct way to get the properties you want. There are a lot of scenarios here. For your exact one, meaning get property from parent interface, you can do something like:
private static PropertyInfo FindMyProperty(Type type, string propertyName)
{
var result = type.GetProperty(propertyName);
if (result == null)
{
foreach(var iface in type.GetInterfaces())
{
var ifaceProp = FindMyProperty(iface, propertyName);
if (ifaceProp != null)
return ifaceProp;
}
}
return result;
}
DISCLAIMER!
This is by no means the best way to get a property from a type, but it should work in your case. You should google around to find an implementation for FindMyProperty that satisfies all your requirements :)

Can I evaluate an expression in a way to determine and possibly set a property that is null?

I have a service that takes an object and based on the properties within will perform different actions; with this any of these properties can be null, meaning don't perform this action.
I am trying to create a very simple to use API to do this in cases where some properties can be multiple levels deep, here is an example of the current implementation
service.PerformActions(DataFactory.GetNewData<ActionsInfo> (
data => data.SomeParent = DataFactory.GetNewData<SomeParentInfo>(),
data => data.SomeParent.SomeProperty = "someValue" ));
This is a slightly simplified version and in real cases I some times have to setup multiple parent properties this way in order to set one string property at the bottom.
What I would like to do is adjust the code within the GetNewData method to handle instantiating these properties as needed so that the code could look like this:
service.PerformActions(DataFactory.GetNewData<ActionsInfo> (
data => data.SomeParent.SomeProperty = "someValue" ));
Here is my current code for GetNewData:
public static T GetNewData<T>(params Action<T>[] actions)
{
var data = Activator.CreateInstance<T>();
foreach (var action in actions)
{
try
{
action(data);
}
catch (NullReferenceException)
{
throw new Exception("The property you are attempting to set is within a property that has not been set.");
}
}
return data;
}
My first thought is to change the params array to be Expression<Action<T>>[] actions and somehow get a member expression for any of these parents that are null, which would allow me to use the activator to create an instance. However my experience with the more advanced features of Expression trees is slim at best.
The reason for attempting to make this API as simplistic as possible is that it is a UI testing framework that will eventually be used by non developers.
Edit:
I want to add one further example of the current implementation to hopefully demonstrate that what I'm trying to do will provide for more readable code, yes there is a very slight 'side-effect' if I can pull this off but I would argue it is a helpful one.
ExampleDataFactory.GetNewData<ServicesAndFeaturesInfo>(
x => x.Property1 = ExampleDataFactory.GetNewData<Property1Type>(),
x => x.Property1.Property2 = ExampleDataFactory.GetNewData<Property2Type>(),
x => x.Property1.Property2.Property3 = ExampleDataFactory.GetNewData<Property3Type>(),
x => x.Property1.Property2.Property3.Property4 = true);
Edit 2:
The classes that I'm working with here are generated from Apache Thrift struct definitions and as such I have no control over them to be able to set up some kinda of smart constructor.
After getting an answer on my other question I now have a fully working solution for this, it isn't quite as simple syntax as I was originally aiming for, but it isn't bad.
public static DataBuilder<T> GetNewData<T>() where T : class, new()
{
return new DataBuilder<T>();
}
The DataBuilder Class:
public class DataBuilder<T>
{
public readonly T data;
public DataBuilder()
{
data = Activator.CreateInstance<T>();
}
public DataBuilder(T data)
{
this.data = data;
}
public DataBuilder<T> SetValue<T2>(Expression<Func<T, T2>> expression, T2 value)
{
var mExpr = GetMemberExpression(expression);
var obj = Recurse(mExpr);
var p = (PropertyInfo)mExpr.Member;
p.SetValue(obj, value);
return this;
}
public T Build()
{
return data;
}
public object Recurse(MemberExpression expr)
{
if (expr.Expression.Type != typeof(T))
{
var pExpr = GetMemberExpression(expr.Expression);
var parent = Recurse(pExpr);
var pInfo = (PropertyInfo) pExpr.Member;
var obj = pInfo.GetValue(parent);
if (obj == null)
{
obj = Activator.CreateInstance(pInfo.PropertyType);
pInfo.SetValue(parent, obj);
}
return obj;
}
return data;
}
private static MemberExpression GetMemberExpression(Expression expr)
{
var member = expr as MemberExpression;
var unary = expr as UnaryExpression;
return member ?? (unary != null ? unary.Operand as MemberExpression : null);
}
private static MemberExpression GetMemberExpression<T2>(Expression<Func<T, T2>> expr)
{
return GetMemberExpression(expr.Body);
}
}
The Usage:
ExampleDataFactory.GetNewData<ServicesAndFeaturesInfo>()
.SetValue(x=> x.Property1.EnumProperty, EnumType.Own)
.SetValue(x=> x.Property2.Property3.Property4.BoolProperty, true)
.Build();
I think you could use the ExpandoObject or the ElasticObject.
ExpandoObject as far as I know it will get "transformed" into a dictionary ( Properties => Values ).

Access a type-safe enumeration of a class's members

My class MyClass<t> has the following method
private static bool TypeHasProperty(string NameOfPropertyToMatch)
{
var properties = typeof(t).GetProperties();
var requiredProperty = properties.First(
a => a.Name == NameOfPropertyToMatch);
if (requiredProperty == null)
{
return false
};
return true;
}
This is called at the start of a static method:
MyClass<t>.SendToJavaScript(t InstanceOfType, string NameOfPropertyWithinType).
to make sure that InstanceOfType actually has a property of that name before continuing, because otherwise an exception wouldn't get thrown until waaaaay down the line
(this information will eventually get serialized and sent to a Javascript Application, which needs to know which property to access in order to do it's job).
I would like for a good, type-safe way to call that method that will not cause the validation to fail if I decide to change the name of my properties later on down the line.
For instance, I do NOT want to call it like this:
MyClass<Person>.SendToJavaScript(MyPerson, "Name")
Because if I decide to change Name to LastName, in the Person class, I would have to make sure to come in and change that magic string.
What I WOULD want, is something like this:
MeClass<Person>.SendToJavaScript(
MyPerson,
TypeOf(Person).Properties.Name.ToString())
so that if I use Refactoring to change Name to LastName, the IDE will sweep through there and change my call as well.
Does anything like that exist? Or do I just need to be careful when I refactor? (I love me some F2)
We use something like this in our code:
private static bool TypeHasProperty<P>(Expression<Func<T, P>> accessor)
{
var propertyName = ((MemberExpression)accessor.Body).Member.Name;
var properties = typeof(T).GetProperties();
return properties.Any(a => a.Name == propertyName);
}
Use it:
class Foo
{
public int Baz { get; set; }
}
MyClass<Foo>.TypeHasProperty(f => f.Baz);
Edit: Changed the query to use Any() as it expresses better what you actually want to do (does a property with the given name exist?)
Note that First() will throw if no match was found.
Try this:
MeClass<Person>.SendToJavaScript(MyPerson, x => x.Name);
And then:
private static bool TypeHasProperty(Expression propertyExpression)
{
var expression = GetMemberInfo(propertyExpression);
string name = expression.Member.Name;
return typeof(t).GetProperty(name) != null;
}
private static MemberExpression GetMemberInfo(Expression method)
{
LambdaExpression lambda = method as LambdaExpression;
if (lambda == null)
throw new ArgumentNullException("method");
MemberExpression memberExpr = null;
if (lambda.Body.NodeType == ExpressionType.Convert)
{
memberExpr =
((UnaryExpression)lambda.Body).Operand as MemberExpression;
}
else if (lambda.Body.NodeType == ExpressionType.MemberAccess)
{
memberExpr = lambda.Body as MemberExpression;
}
if (memberExpr == null)
throw new ArgumentException("method");
return memberExpr;
}
Taken shamelessly from here: Retrieving Property name from lambda expression
But this is a slightly different question, so I've posted a complete answer.
I've just noticed that in the link I've provided the second answer is much simpler to this. This is covered by ChrisWue'a answer in this thread.

Categories

Resources