score:0

Here's a working program that does what I think you'd like. It defines a function that takes a path to an integer property inside a collection, and an integer value. It then checks whether or not that collection has Count > 0 of that value.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Linq.Expressions;
using System.Reflection;
using System.Collections;

namespace Test_Console
{
    public class Subscription
    {
        public int Id { get; set; }
        public Client Client { get; set; }
    }

    public class Client
    {
        public ICollection<Invoice> Invoices { get; set; }
    }

    public class Invoice
    {
        public int Id { get; set; }
    }

    class Program
    {
        static void Main(string[] args)
        {
            var subscriptions = new[]
            {
                new Subscription { Id = 1, Client = new Client { Invoices = new [] {
                    new Invoice { Id = 1 },
                    new Invoice { Id = 2 },
                    new Invoice { Id = 5 }
                } } },
                new Subscription { Id = 2, Client = new Client { Invoices = new [] {
                    new Invoice { Id = 4 },
                    new Invoice { Id = 5 },
                    new Invoice { Id = 5 }
                } } },
                new Subscription { Id = 3, Client = new Client { Invoices = new Invoice[] {
                } } },
            };

            var propertyPath = "Client.Invoices.Id";
            Console.WriteLine("What Id would you like to check " + propertyPath + " for?");
            var propertyValue = int.Parse(Console.ReadLine());
            var whereNumberOne = makeWhere<Subscription>(propertyPath, propertyValue);

            Console.WriteLine("The following Subscription objects match:");
            foreach (var s in subscriptions.Where(whereNumberOne).ToList())
            {
                Console.WriteLine("Id: " + s.Id);
            }
        }

        private static Func<T, bool> makeWhere<T>(string propertyPath, int propertyValue)
        {
            string[] navigateProperties = propertyPath.Split('.');

            var currentType = typeof(T);
            var functoidChain = new List<Func<object, object>>();
            functoidChain.Add(x => x);  // identity function starts the chain
            foreach (var nextProperty in navigateProperties)
            {
                // must be inside loop so the closer on the functoids works properly
                PropertyInfo nextPropertyInfo;

                if (currentType.IsGenericType
                 && currentType.GetGenericTypeDefinition().GetInterfaces().Contains(typeof(IEnumerable)))
                {
                    nextPropertyInfo = currentType.GetGenericArguments()[0].GetProperty(nextProperty);
                    functoidChain.Add(x =>
                        ((IEnumerable<object>)x)
                        .Count(y => (int)nextPropertyInfo.GetValue(y, null) == propertyValue)
                    );
                }
                else
                {
                    nextPropertyInfo = currentType.GetProperty(nextProperty);
                    functoidChain.Add(x => nextPropertyInfo.GetValue(x, null));
                }
                currentType = nextPropertyInfo.PropertyType;
            }
            // compose the functions together
            var composedFunctoidChain = functoidChain.Aggregate((f, g) => x => g(f(x)));
            var leftSide = new Func<T, int>(x => (int)composedFunctoidChain(x));
            return new Func<T, bool>(r => leftSide(r) > 0);
        }
    }
}

score:0

I think this should get you closer to what you're going for:

static Expression<Func<T, bool>> CreateAnyExpression<T, T2>(string propertyPath, 
                                    Expression<Func<T2, bool>> matchExpression)
{
    var type = typeof(T);
    var parameterExpression = Expression.Parameter(type, "s");
    var propertyNames = propertyPath.Split('.');
    Expression propBase = parameterExpression;
    foreach(var propertyName in propertyNames)
    {
        PropertyInfo property = type.GetProperty(propertyName);
        propBase = Expression.Property(propBase, property);
        type = propBase.Type;
    }
    var itemType = type.GetGenericArguments()[0];
    // .Any(...) is better than .Count(...) > 0
    var anyMethod = typeof(Enumerable).GetMethods()
        .Single(m => m.Name == "Any" && m.GetParameters().Length == 2)
        .MakeGenericMethod(itemType);
    var callToAny = Expression.Call(anyMethod, propBase, matchExpression);
    return Expression.Lambda<Func<T, bool>>(callToAny, parameterExpression);
}

Calling it like this:

CreateAnyExpression<Subscription, Invoice>("Client.Invoices", i => i.InvoiceID == 1)

... yields the following Expression<Func<Subscription,bool>>:

s => s.Client.Invoices.Any(i => (i.InvoiceID == 1)) 

score:0

Here's a working program building Linq Expression

{(x.Children.Count(y => y.SomeID == SomeVar) > 0)}
using System;
using System.Linq;
using System.Linq.Expressions;

namespace ExpressionTree
{
    class Program
    {
        static void Main(string[] args)
        {
            ParameterExpression foundX = Expression.Parameter(typeof(Parent), "x");
            Guid[] guids = new Guid[1] { Guid.NewGuid() };

            Expression expression = GetCountWithPredicateExpression(guids, foundX);
        }

        private static Expression GetCountWithPredicateExpression(Guid[] idsToFilter, ParameterExpression foundX)
        {
            System.Reflection.PropertyInfo childIDPropertyInfo = typeof(Child).GetProperty(nameof(Child.SomeID));
            ParameterExpression foundY = Expression.Parameter(typeof(Child), "y");

            Expression childIDLeft = Expression.Property(foundY, childIDPropertyInfo);
            Expression conditionExpression = Expression.Constant(false, typeof(bool));

            foreach (Guid id in idsToFilter)
                conditionExpression = Expression.Or(conditionExpression, Expression.Equal(childIDLeft, Expression.Constant(id)));

            Expression<Func<Child, bool>> idLambda = Expression.Lambda<Func<Child, bool>>(conditionExpression, foundY);

            var countMethod = typeof(Enumerable).GetMethods()
                .First(method => method.Name == "Count" && method.GetParameters().Length == 2)
                .MakeGenericMethod(typeof(Child));

            System.Reflection.PropertyInfo childrenPropertyInfo = typeof(Parent).GetProperty("Children");
            Expression childrenLeft = Expression.Property(foundX, childrenPropertyInfo);

            Expression ret = Expression.GreaterThan(Expression.Call(countMethod, childrenLeft, idLambda), Expression.Constant(0));

            return ret;
        }
    }

    public class Parent
    {
        public Child[] Children { get; set; }
    }

    public class Child
    {
        public int ID { get; set; }
        public Guid SomeID { get; set; }
    }
}

Related Articles