Eagle
Eagle

Reputation: 481

How to use Expression for Dictionary?

Let me re-post my question.

Here is my static class to wrap some call to Async

public static class QueryExtensions
{
    internal static async Task<List<TSource>> ToListAsync<TSource>(this IQueryable<TSource> query)
    {
        //Initialzie
        var call = Expression.Call(typeof(Enumerable), nameof(Enumerable.ToList), new Type[] { typeof(T) }, query.Expression);

        //Execute
        return await ExecuteAsync<List<TSource>>(call);
    }

    public static async Task<Dictionary<TKey, TSource>> ToDictionaryAsyn<TKey, TSource>(this IQueryable<TSource> query, Func<TSource, TKey> keySelector)
    {
        //I don't know how to pass keySelector to following 
        var call = Expression.Call(typeof(Enumerable), nameof(Enumerable.ToDictionary), new Type[] { typeof(TKey), typeof(TSource) }, query.Expression);

        //Execute
        return await ExecuteAsync<Dictionary<TKey, TSource>>(call);
    }

    private static Task<TResult> ExecuteAsync<TResult>(MethodCallExpression expression)
    {
        //Do some in Asyn
    }
}

I will call it in following code

        await list.AsQueryable().ToListAsync(); -- It work now
        await list.AsQueryable().ToDictionaryAsyn(current => current.Name);

I don't know how to implement ToDictionaryAsyn method

Upvotes: 0

Views: 345

Answers (2)

user10902438
user10902438

Reputation:

An example as below. Hope it helps.

public static class Extns
    {
        public static Dictionary<TKey, TSource> ToDictWithSelector<TKey, TSource>(this IQueryable<TSource> query,
                                                                  Expression<Func<TSource, TKey>> keySelector)
        {
            var p = Expression.Parameter(typeof(IQueryable<TSource>));
            var call = Expression.Call(typeof(Enumerable),
                                      nameof(Enumerable.ToDictionary),
                                      new[] { typeof(TSource), typeof(TKey) },
                                      new Expression[] { p, keySelector });
            var lambda = Expression.Lambda<Func<IQueryable<TSource>, Dictionary<TKey, TSource>>>(call, p);
            Func<IQueryable<TSource>, Dictionary<TKey, TSource>> func = lambda.Compile();
            return func(query.AsQueryable());
            
        }
    }

// usage of the above extension
static void Main(string[] args)
{
    var list = new List<People>();
    list.Add(new People() { Name = "Eagle", Age = 18 });
    list.Add(new People() { Name = "Sam", Age = 30 });
    list.Add(new People() { Name = "May", Age = 24 });

    Expression<Func<People, string>> selector = p => p.Name;
    Dictionary<string, People> rr1 = list.AsQueryable().ToDictWithSelector(selector);

    Expression<Func<People, int>> selector2 = p => p.Age;
    Dictionary<int, People> rr2 = list.AsQueryable().ToDictWithSelector(selector2);
}

Upvotes: 0

Guru Stron
Guru Stron

Reputation: 142943

If your goal is to generate something like (List<People> l) => l.ToDictionary(p => p.Name) you can achieve it like this:

// declare parameter of type IEnumerable<People>
var p = Expression.Parameter(typeof(IEnumerable<People>));

// find Enumerable.ToDictionary(this list, keySelector)
var method = typeof(Enumerable)
    .GetMethods()
    .Where(mi => mi.Name == nameof(Enumerable.ToDictionary) && mi.GetParameters().Length == 2)
    .Single();

// apply generic parameters
var concreteMethod = method.MakeGenericMethod(new[] {typeof(People), typeof(string)});

// create selector
Expression<Func<People, string>> selector = p => p.Name;

var call = Expression.Call(null, concreteMethod, p, selector);

// create lambda
var lambda = Expression.Lambda<Func<List<People>, Dictionary<string,People>>>(call, p);

var func = lambda.Compile();
var result = func(list);

Upvotes: 1

Related Questions