2

I have a this method that would generate a query and would export it to excel. I would like the users to choose which columns would they like to be exported on.

my question is kinda similar to this OP LINQ select property by name

Where we can create a LINQ select query in base on list of string[] columns and which should be kinda similar to this _dbcontext.Person.Select( SelectorFunc(columns) ).SomeOtherQuery().ToList(); query and function SelectorFunc(columns) below

public Func<Person, string[]> SelectorFunc(string[] columns) {
    //  ninja code!! 
   return " not sure here";
}

its kinda messy if i would have to do it manually like.

var query = _dbcontext.Person.AsQueryable();

if(column == "ID") 
  query = query.Select( x=x.ID);
if(column == "Name") 
  query = query.Select( x=x.Name);

//and the list goes on...

PS: I really appreciate for the enlightenment and Thank you for your time.

Julius Limson
  • 498
  • 3
  • 19
  • 1
    Your case is Dynamic Select, https://stackoverflow.com/a/45205267/3789481 this answer is separate column fields by ",". So your string[] colums would be string.Join(",", colums ) to join it by comma – Tấn Nguyên Feb 04 '20 at 05:23
  • @TấnNguyên this is what exactly im looking for! thank you – Julius Limson Feb 04 '20 at 05:27
  • I would suggest, generate dynamic SQL query[Select statements or Database Views ] on the basis of Selected Columns to Export and then use sql query via EF / ADO.NET, this will be very easy to handle and maintainable. – shyam sundar singh tomar Feb 04 '20 at 05:33

2 Answers2

4

This is a duplicate question from this OP LINQ : Dynamic select

base on the those answer.

public static Func<T, T> SelectorFunc<T>(string[] columns)
{

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

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

        // create initializers
        var bindings = columns.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();
}

and consuming the function would be _dbcontext.Person.SomeOtherQuery().Select( SelectorFunc<Person>(columns) ).ToList()

Thank you @Tấn Nguyên for your answer.

Julius Limson
  • 498
  • 3
  • 19
0

I think this should be help: I found it from here Dynamically select properties by string names

this is your object:

    public class Foo
{
    public string Property1 {get;set;}
    public string Property2 {get;set;}  
}

this is your helper class:

  public static class ExpressionTreeHelper
{
    /// <summary> Returns an IEnumerable of anonymous type defined by <paramref name="properties"/>. </summary>
    public static IEnumerable<dynamic> SelectDynamic<T>(this IEnumerable<T> source, params string[] properties)
    {
        return SelectProperties<T>(source.AsQueryable(), properties).Cast<dynamic>();
    }

    private static IQueryable SelectProperties<T>(this IQueryable<T> queryable, IEnumerable<string> propertyNames)
    {
        // get propertyinfo's from original type
        var properties = typeof(T).GetProperties().Where(p => propertyNames.Contains(p.Name));

        // Create the x => expression
        var lambdaParameterExpression = Expression.Parameter(typeof(T));
        // Create the x.<propertyName>'s
        var propertyExpressions = properties.Select(p => Expression.Property(lambdaParameterExpression, p));

        // Creating anonymous type using dictionary of property name and property type
        var anonymousType = AnonymousTypeUtils.CreateType(properties.ToDictionary(p => p.Name, p => p.PropertyType));
        var anonymousTypeConstructor = anonymousType.GetConstructors().Single();
        var anonymousTypeMembers = anonymousType.GetProperties().Cast<MemberInfo>().ToArray();

        // Create the new {} expression using 
        var anonymousTypeNewExpression = Expression.New(anonymousTypeConstructor, propertyExpressions, anonymousTypeMembers);

        var selectLambdaMethod = GetExpressionLambdaMethod(lambdaParameterExpression.Type, anonymousType);
        var selectBodyLambdaParameters = new object[] { anonymousTypeNewExpression, new[] { lambdaParameterExpression } };
        var selectBodyLambdaExpression = (LambdaExpression)selectLambdaMethod.Invoke(null, selectBodyLambdaParameters);

        var selectMethod = GetQueryableSelectMethod(typeof(T), anonymousType);
        var selectedQueryable = selectMethod.Invoke(null, new object[] { queryable, selectBodyLambdaExpression }) as IQueryable;
        return selectedQueryable;
    }

    private static MethodInfo GetExpressionLambdaMethod(Type entityType, Type funcReturnType)
    { 
        var prototypeLambdaMethod = GetStaticMethod(() => Expression.Lambda<Func<object, object>>(default(Expression), default(IEnumerable<ParameterExpression>))); 
        var lambdaGenericMethodDefinition = prototypeLambdaMethod.GetGenericMethodDefinition(); 
        var funcType = typeof(Func<,>).MakeGenericType(entityType, funcReturnType); 
        var lambdaMethod = lambdaGenericMethodDefinition.MakeGenericMethod(funcType); 
        return lambdaMethod; 
    } 

    private static MethodInfo GetQueryableSelectMethod(Type entityType, Type returnType)
    { 
        var prototypeSelectMethod = GetStaticMethod(() => Queryable.Select(default(IQueryable<object>), default(Expression<Func<object, object>>))); 
        var selectGenericMethodDefinition = prototypeSelectMethod.GetGenericMethodDefinition();
        return selectGenericMethodDefinition.MakeGenericMethod(entityType, returnType);
    } 

    private static MethodInfo GetStaticMethod(Expression<Action> expression)
    { 
        var lambda = expression as LambdaExpression; 
        var methodCallExpression = lambda.Body as MethodCallExpression; 
        return methodCallExpression.Method; 
    } 
}

and call by this way:

public static void Main()
{
    var list = new[] {new Foo { Property1 = "foo1" }, new Foo { Property1 = "foo2" }};

    var result = list.SelectDynamic(nameof(property).toString());

    result.Dump();
}