0

I'm trying to figure out for a IQueryable how I can build a csv file by dynamically selecting objects as strings.

for example:

I read this about dynamically selecting properties of a T ...

LINQ : Dynamic select

That would allow me to do something like this ...

var data = new List<T> { items };
            var fields = new string[] { "Field1", "Field2", "Field3" };
            // build row strings
            var rows = set.Select(BuildRowObjectExpression<T, ProjectionOfT>(fields))
                .Select(i => Serialise<ProjectionOfT>(i));


        string Serialise<T>(T i, string separator)
        {
            var properties = typeof(T).GetProperties();
            var values = new List<string>();
            foreach (var p in properties)
                values.Add(p.GetValue(i).ToString());

            return string.Join(separator, values);
        }

        Func<T, Tout> BuildRowObjectExpression<T, Tout>(string[] fields)
        {
            // input parameter "o"
            var xParameter = Expression.Parameter(typeof(T), "o");

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

            // create initializers
            var bindings = fields.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 T { Field1 = o.Field1, Field2 = o.Field2 }"
            var xInit = Expression.MemberInit(xNew, bindings);

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

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

What I was wondering however is:

How do I build this as an expression / func that I can use with an IQueryable to do something like this

// this would build me a string array from the specified properties 
// in a collection of T joining the values using the given separator 
var results = data.Select(i => BuildString(fields, "|")).ToArray();

I would ideally like to use this with an entity set.

Community
  • 1
  • 1
War
  • 8,539
  • 4
  • 46
  • 98

1 Answers1

0

String conversion/concatenation is not a database job. You'd better keep the two parts separate - data retrieval in database query and data transformation in memory query.

For instance, you can use the following custom extensions methods:

public static class Extensions
{
    public static IQueryable<T> Select<T>(this IQueryable source, string[] fields)
    {
        var parameter = Expression.Parameter(source.ElementType, "o");
        var body = Expression.MemberInit(
            Expression.New(typeof(T)),
            fields.Select(field => Expression.Bind(
                typeof(T).GetProperty(field),
                Expression.PropertyOrField(parameter, field))
            )
        );
        var selector = Expression.Lambda(body, parameter);
        var expression = Expression.Call(
            typeof(Queryable), "Select", new[] { parameter.Type, body.Type },
            source.Expression, Expression.Quote(selector)
        );
        return source.Provider.CreateQuery<T>(expression);
    }

    public static IEnumerable<string> Serialize<T>(this IEnumerable<T> source, string separator)
    {
        var properties = typeof(T).GetProperties();
        return source.Select(item => string.Join(separator, properties.Select(property => property.GetValue(item))));
    }
}

like this

var results = db.Data.Select<ProjectionOfT>(fields).Serialize("|");

If you want to avoid the ProjectionOfT class, there is no easy way to do that since it requires dynamic runtime class generation, so you'd better resort to System.Linq.Dynamic package.

Ivan Stoev
  • 195,425
  • 15
  • 312
  • 343
  • Gah, it doesn't solve my actual problem but I don;t think it was a well worded question, that said it does solve the problem presented in the question. Thx :) – War May 11 '16 at 14:02