MemberExpression, build Expression.Property from class
Asked Answered
B

1

6

Below expression compares property NAME with the value PETER.

            ParameterExpression pe = Expression.Parameter(typeof(T), "x");
            MemberExpression member = Expression.Property(pe, "name");
            ConstantExpression value = Expression.Constant("Peter");
            exp = Expression.Equal(member, value);

What if the property is a class:

            public class Address
            {
                public string Name {get; set;}
            }

Then the expression would look something similar to this:

            MemberExpression member = Expression.Property(pe, "Address.Name");
            ConstantExpression value = Expression.Constant("Peter");
            exp = Expression.Equal(member, value);

This would fail because the member type doesn't match the value type.

So, the question is: How to build an Expression that would work using the above class sample ??

I use this expression in a NHibernate.Linq query:

        var q = from f in data //of type IQueryable<T>
            select f;
        if (filter != null) //filter of type Expression<Func<T, bool>>
            q = q.Where(filter);
        etc....

Thank you.

UPDATE by Peter:

Based on the code from xanatos (next post) I have created the following test to understand how it works. Its not very different from what xanatos do, but at first I could not get it to work, so I decided to write it allover in one simple test, and that did it. With thanks to xanatos:

    [Test]
    public void FilterWithDeepProperties()
    {
        //Arrange
        IGenericGridRepository repository = ObjectFactory.GetInstance<IGenericGridRepository>();

        FilterDescriptor filter = new FilterDescriptor("AgreementId.Name", FilterOperator.IsEqualTo, "a name");
        string[] properties = filter.Member.Split('.');
        ParameterExpression pe = Expression.Parameter(typeof(SampleDomain), "x");

        //Act
        Expression lastMember = pe;
        for (int i = 0; i < properties.Length; i++)
        {
            MemberExpression member = Expression.Property(lastMember, properties[i]);
            lastMember = member;
        }
        ConstantExpression valueExpression = Expression.Constant(filter.Value);
        Expression equalityExpression = Expression.Equal(lastMember, valueExpression);
        Expression<Func<SampleDomain, bool>> where = Expression.Lambda<Func<SampleDomain, bool>>(equalityExpression, pe);
        var result = repository.GetObjects<SampleDomain>(filter: where);

        //Assert
        result.Count().Should().BeGreaterThan(0, "because there are many schedule items equals to " + filter.Value);
    }
Bail answered 16/3, 2015 at 11:11 Comment(2)
You are looking from the wrong point of view. typeof(T) is already a type (a class for example), so in your example typeof(T) is typeof(Address)... unless there is an over-object Person that has an Address that has a string Name...Montford
I get it, but it is as you end up with (the over-object), like the following sample: select t.*, a.name from SomeTable t inner join AddressTable a on a.Id = t.AddressTableId where a.name = 'Peter'.Mariel
M
7

You probably want something like:

public static Expression<Func<TSource, bool>> GetEquality<TSource>(object value, params string[] properties)
{
    ParameterExpression pe = Expression.Parameter(typeof(TSource), "source");

    Expression lastMember = pe;

    for (int i = 0; i < properties.Length; i++)
    {
        MemberExpression member = Expression.Property(lastMember, properties[i]);
        lastMember = member;
    }

    Expression valueExpression = Expression.Constant(value);
    Expression equalityExpression = Expression.Equal(lastMember, valueExpression);
    Expression<Func<TSource, bool>> lambda = Expression.Lambda<Func<TSource, bool>>(equalityExpression, pe);
    return lambda;
}

Use it like:

Expression exp = GetEquality<Person>("Foo", "Address", "Name");

Where Foo is your Peter (so the value that must be compared), while Address and Name are the names of the "chain" of properties. For example I'm using

public class Person
{
    public Address Address { get; set; }
}

public class Address
{
    public string Name { get; set; }
}

So the expression generated is

source.Address.Name == "Foo"

If you want to use something like Address.Name, you can use the method like

Expression exp = GetEquality<Person>("Foo", "Address.Name".Split('.'));
Montford answered 16/3, 2015 at 12:20 Comment(4)
This does looks promising - I gotta try it to fully understand it :-) What type would TSource be (my parent class?) ??Mariel
@PeterLarsen'CPH' What you called T in typeof(T). I called it TSource because in version 0.1 of the code there were a TSource and a TTarget for the value. Then TTarget was removed :)Montford
OK, let me try it. Thanks so far :-)Mariel
Thanks for your help. I think there might be an error somewhere in the code since I could not get it to work at first ;-), but it's not really important since your code set me off in the right direction :-)Mariel

© 2022 - 2024 — McMap. All rights reserved.