Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

LINQ : Dynamic select

Consider we have this class :

    public  class Data
{
    public string Field1 { get; set; }
    public string Field2 { get; set; }
    public string Field3 { get; set; }
    public string Field4 { get; set; }
    public string Field5 { get; set; }

}

How do I dynamically select for specify columns ? something like this :

  var list = new List<Data>();

  var result= list.Select("Field1,Field2"); // How ?

Is this the only solution => Dynamic LINQ ?
Selected fields are not known at compile time. They would be specified at runtime

like image 252
Unforgiven Avatar asked May 13 '13 07:05

Unforgiven


People also ask

Can you use LINQ on dynamic?

The Dynamic source file includes a helper library that allows you to express LINQ queries using extension methods that take string arguments instead of type safe operators. To use the Dynamic Expression API, you could simply copy/paste the Dynamic source file in your project.

What is Dynamic LINQ?

The Dynamic LINQ library exposes a set of extension methods on IQueryable corresponding to the standard LINQ methods at Queryable, and which accept strings in a special syntax instead of expression trees.

What is LINQ select?

The Select() method invokes the provided selector delegate on each element of the source IEnumerable<T> sequence, and returns a new result IEnumerable<U> sequence containing the output of each invocation.

How does LINQ select work?

Select is used to project individual element from List, in your case each customer from customerList . As Customer class contains property called Salary of type long, Select predicate will create new form of object which will contain only value of Salary property from Customer class.


2 Answers

You can do this by dynamically creating the lambda you pass to Select:

Func<Data,Data> CreateNewStatement( string fields )
{
    // input parameter "o"
    var xParameter = Expression.Parameter( typeof( Data ), "o" );

    // new statement "new Data()"
    var xNew = Expression.New( typeof( Data ) );

    // create initializers
    var bindings = fields.Split( ',' ).Select( o => o.Trim() )
        .Select( o => {

            // property "Field1"
            var mi = typeof( Data ).GetProperty( o );

            // original value "o.Field1"
            var xOriginal = Expression.Property( xParameter, mi );

            // set value "Field1 = o.Field1"
            return Expression.Bind( mi, xOriginal );
        }
    );

    // initialization "new Data { Field1 = o.Field1, Field2 = o.Field2 }"
    var xInit = Expression.MemberInit( xNew, bindings );

    // expression "o => new Data { Field1 = o.Field1, Field2 = o.Field2 }"
    var lambda = Expression.Lambda<Func<Data,Data>>( xInit, xParameter );

    // compile to Func<Data, Data>
    return lambda.Compile();
}

Then you can use it like this:

var result = list.Select( CreateNewStatement( "Field1, Field2" ) );
like image 72
Nick Butler Avatar answered Oct 13 '22 14:10

Nick Butler


In addition for Nicholas Butler and the hint in comment of Matt(that use T for type of input class), I put an improve to Nicholas answer that generate the property of entity dynamically and the function does not need to send field as parameter.

For Use add class as below:

public static class Helpers
{
    public static Func<T, T> DynamicSelectGenerator<T>(string Fields = "")
    {
        string[] EntityFields;
        if (Fields == "")
            // get Properties of the T
            EntityFields = typeof(T).GetProperties().Select(propertyInfo => propertyInfo.Name).ToArray();
        else
            EntityFields = Fields.Split(',');

        // input parameter "o"
        var xParameter = Expression.Parameter(typeof(T), "o");

        // new statement "new Data()"
        var xNew = Expression.New(typeof(T));

        // create initializers
        var bindings = EntityFields.Select(o => o.Trim())
            .Select(o =>
            {

                // property "Field1"
                var mi = typeof(T).GetProperty(o);

                // original value "o.Field1"
                var xOriginal = Expression.Property(xParameter, mi);

                // set value "Field1 = o.Field1"
                return Expression.Bind(mi, xOriginal);
            }
        );

        // initialization "new Data { Field1 = o.Field1, Field2 = o.Field2 }"
        var xInit = Expression.MemberInit(xNew, bindings);

        // expression "o => new Data { Field1 = o.Field1, Field2 = o.Field2 }"
        var lambda = Expression.Lambda<Func<T, T>>(xInit, xParameter);

        // compile to Func<Data, Data>
        return lambda.Compile();
    }
}

The DynamicSelectGenerator method get entity with type T, this method have optional input parameter Fields that if you want to select special field from entity send as a string such as "Field1, Field2" and if you don't send anything to method, it returns all of the fields of entity, you could use this method as below:

 using (AppDbContext db = new AppDbContext())
            {
                //select "Field1, Field2" from entity
                var result = db.SampleEntity.Select(Helpers.DynamicSelectGenerator<SampleEntity>("Field1, Field2")).ToList();

                //select all field from entity
                var result1 = db.SampleEntity.Select(Helpers.DynamicSelectGenerator<SampleEntity>()).ToList();
            }

(Assume that you have a DbContext with name AppDbContext and the context have an entity with name SampleEntity)

like image 12
Ali Avatar answered Oct 13 '22 16:10

Ali