Yogesh
Yogesh

Reputation: 3076

How to get enumerable or collection of values from WhereSelectListIterator

I need to write a generic method for getting distinct values and propertyName is not known in advance. I want to do it using the LINQ expression. I am trying the below way but when getting the result from SelectMethod invoke I am getting WhereSelectListIterator. I want to convert it to IEnumerable so I can call the Distinct method. But I am not to cast it to IEnumerable(as it's not implemented it). How to get Enumerable back from WhereSelectListIterator or is there any way I can get IEnumerable directly from invoke of generic method.

enter image description here

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

namespace ConsoleApp10 {

public class EmployeeEqualityComparer : IEqualityComparer<Employee>
{
    public bool Equals(Employee x, Employee y)
    {
        return x.Id == y.Id;
    }

    public int GetHashCode(Employee obj)
    {
        return obj.Id;
    }
}
class Program
{
    static void Main(string[] args)
    {
        var employees = new List<Employee>()
        { new Employee(){Id=1},
          new Employee(){Id=2},
        new Employee(){Id=1}};

       var values1 =  employees.Select(obj => obj.Id).Distinct();
       var values2 =  employees.Distinct(new EmployeeEqualityComparer());
     var values=   GetDistinctValue(employees, "Id");
    }


    private static readonly MethodInfo DistinctMethod = typeof(Enumerable).GetMethods().First(method =>
     method.Name == "Distinct" &&
     method.GetParameters().Length == 1);

    private static readonly MethodInfo SelectMethod = typeof(Enumerable).GetMethods().First(method =>

method.Name == "Select" && method.GetParameters().Length == 2);

    public static IEnumerable<object> GetDistinctValue<T>(IEnumerable<T> records, string propertyName)
    {
        try
        {
            ParameterExpression parameterExpression = Expression.Parameter(typeof(T));
            Expression propertyExpression = Expression.Property(parameterExpression, propertyName);
           var lambda = Expression.Lambda(propertyExpression, parameterExpression);
            var propertyType = propertyExpression.Type;

           // MethodCallExpression compareCall = Expression.Call(typeof(Program), "Compare", Type.EmptyTypes, propertyExpression, Expression.Constant(""), Expression.Constant(""), Expression.Constant(""));


            //LambdaExpression lambda = Expression.Lambda<Func<T, bool>>(compareCall, parameterExpression);
            MethodInfo genericMethod = SelectMethod.MakeGenericMethod(typeof(T),propertyType);

            var result = genericMethod.Invoke(null, new object[] { records, lambda.Compile() });
            

            MethodInfo distinctGenericMethod = DistinctMethod.MakeGenericMethod(result.GetType());

            var finalResult = distinctGenericMethod.Invoke(null, new object[] { result});


            return null;
        }
        catch(Exception exception)
        {
            Console.WriteLine(exception.Message);
        }
        return null;
    }


}


public class Employee
{
    public int Age { get; set; }

    public string Name { get; set; }

    public int Id { get; set; }
}

}

Upvotes: 0

Views: 775

Answers (1)

Svyatoslav Danyliv
Svyatoslav Danyliv

Reputation: 27461

This is working version of GetDistinctValue. Main idea that you can work with IEnumerable via IQueryable which is dynamic by default.

public static IEnumerable<object> GetDistinctValue<T>(IEnumerable<T> records, string propertyName)
{
    var parameterExpression = Expression.Parameter(typeof(T), "e");

    var body = (Expression)Expression.Property(parameterExpression, propertyName);
    if (body.Type != typeof(object))
    {
        body = Expression.Convert(body, typeof(object));
    }

    var lambda = Expression.Lambda(body, parameterExpression);

    // turn IEnumerable into IQueryable
    var queryable = records.AsQueryable();

    var queryExpression = queryable.Expression;

    // records.Select(e => (object)e.propertyName)
    queryExpression = Expression.Call(typeof(Queryable), nameof(Queryable.Select),
        new[] { typeof(T), typeof(object) }, queryExpression, lambda);

    // records.Select(e => (object)e.propertyName).Distinct()
    queryExpression = Expression.Call(typeof(Queryable), nameof(Queryable.Distinct), new[] { typeof(object) },
        queryExpression);

    // creating IQueryable<object> from generated expression
    var resultQuery = queryable.Provider.CreateQuery<object>(queryExpression);

    // turn IQueryable into IEnumerable
    return resultQuery.AsEnumerable();
}

Upvotes: 0

Related Questions