Dynamic Linq to sql

I recently stumble upon a problem where I had to create dynamic Linq. It was necessary to do so mainly because we wanted to improve the performance of query which was being generated by Linq to sql. Normally I would use if then else to prepare my selection (where clause) or projection (select) dynamically.

But this time the projection that I had to create had so many possibilities that building it using if then else approach means a lot of ugly code. So I decided to create it dynamically without a lot of if then else.

Since I have to create projection dynamically, I started with creating a method that returns dynamic projection selector based on certain parameters. Since the column that I wanted to select may differ in each query, I have to create different func based on parameter. So I have written a method that creates dynamic expression based on input parameter and then compile it to func which can be used as selector.

private Func GetDynamicMappingFunc(string[] sourceFields, string[] targetFields)
{
    //Objective: To generate a function dynamically that looks something like:
    //o => new T1 { SomeText1 =  o.SomeTextEN, SomeText2 = o.SomeTextPT }

    //"o"
    var sourceParameterExpression = Expression.Parameter(typeof(T), "o");

    //"new T1()"
    var targetNewExpression = Expression.New(typeof(T1));

    var targetMemberBindings = new List<MemberBinding>();
    for(int i = 0; i < sourceFields.Length; ++i) {

        //"SomeTextEN"
        //"SomeTextPT"
        var sourcePropertyInfo = typeof(T).GetProperty(sourceFields[i]);

        //"SomeText1"
        //"SomeText2"
        var targetPropertyInfo = typeof(T1).GetProperty(targetFields[i]);

        //"o.SomeTextEN"
        //"o.SomeTextPT"
        var sourceMemberExpression = Expression.Property(sourceParameterExpression, sourcePropertyInfo);

        //"SomeText1 = o.SomeTextEN"
        //"SomeText2 = o.SomeTextPT"
        var targetMemberAssignmentExpression = Expression.Bind(targetPropertyInfo, sourceMemberExpression);

        targetMemberBindings.Add(targetMemberAssignmentExpression);
    }

    //new T1 { SomeText1 =  o.SomeTextEN, SomeText2 = o.SomeTextPT }
    var targetMemberInitExpression = Expression.MemberInit(targetNewExpression, targetMemberBindings);

    //"o => new T1 { SomeText1 =  o.SomeTextEN, SomeText2 = o.SomeTextPT }
    Expression> lambda = Expression.Lambda>(targetMemberInitExpression, sourceParameterExpression);

    //Func
    Func func = lambda.Compile();
    return func;
}

Now I can use it like:


var selector = GetDynamicMappingFunc(new[] { "ShortName", "DescNL" }, new[] { "SomeText1", "SomeText2" });
var res = items.Select(selector);

No comments:

Post a Comment