C#: объединение двух родовых c деревьев выражений - PullRequest
1 голос
/ 02 февраля 2020

У меня есть два дерева выражений: одно для получения свойства навигации класса и другое для фильтрации значений из этого свойства навигации: я пытаюсь их объединить.

class Program
{
    public interface IDeletable
    {
        bool IsDeleted { get; set; }
    }

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

        public IEnumerable<BlogPost> BlogPosts;
    }

    public class BlogPost : IDeletable
    {
        public string Text { get; set; }
        public bool IsDeleted { get; set; }
    }

    static void Main(string[] args)
    {
        var user = new User()
        {
            Id = 1,
            BlogPosts = new List<BlogPost> {
                new  BlogPost {IsDeleted=false,Text="hello" },
                new  BlogPost {IsDeleted=true,Text="this is deleted" }
             }
        };

        Expression<Func<User, IEnumerable<BlogPost>>> notDeletedExpression = Combine<User, BlogPost>(x => x.BlogPosts, x => !x.IsDeleted);

        Console.ReadLine();
    }

    public static Expression<Func<T, IEnumerable<TChild>>> Combine<T, TChild>
      (
      Expression<Func<T, IEnumerable<TChild>>> navigationProperty,
      Expression<Func<TChild, bool>> filter
      )
      where T : class
      where TChild : class, IDeletable
    {
        //TODO  
        // shourld return x=>x.Posts.Where(p=>IsDeleted==false) ; 
        return null;
    }
}

1 Ответ

2 голосов
/ 03 февраля 2020

В приведенном ниже примере два выражения из вашего образца объединяются с использованием метода Enumerable.Where:

public static Expression<Func<T, IEnumerable<TChild>>> Combine<T, TChild>
(
  Expression<Func<T, IEnumerable<TChild>>> navigationProperty,
  Expression<Func<TChild, bool>> filter
)
  where T : class
  where TChild : class, IDeletable
{
    // Consider caching the MethodInfo object: 
    var whereMethodInfo = GetEnumerableWhereMethodInfo<TChild>();

    // Produce an Expression tree like:
    // Enumerable.Where(<navigationProperty>, <filter>)
    var filterExpr = Expression
        .Call(
            whereMethodInfo,
            navigationProperty.Body,
            filter
        );

    // Create a Lambda Expression with the parameters
    // used for `navigationProperty` expression
    return Expression
        .Lambda<Func<T, IEnumerable<TChild>>>(
            filterExpr,
            navigationProperty.Parameters
        );
}

private static MethodInfo GetEnumerableWhereMethodInfo<TSource>()
{
    // Get a MethodInfo definition for `Enumerable.Where<>`:
    var methodInfoDefinition = typeof(Enumerable)
       .GetMethods()
       .Where(x => x.Name == nameof(Enumerable.Where))
       .First(x =>
       {
           var parameters = x.GetParameters();

           return
               parameters.Length == 2 &&
               parameters[0].ParameterType.GetGenericTypeDefinition() == typeof(IEnumerable<>) &&
               parameters[1].ParameterType.GetGenericTypeDefinition() == typeof(Func<,>);
       });

    // Get a MethodInfo object for `Enumerable.Where<TSource>`:
    var methodInfo = methodInfoDefinition.MakeGenericMethod(typeof(TSource));

    return methodInfo;
}
...