8

I have code which builds list only with one property "Name". How to modify the code so it can build list with two properties "Name" and "Test_Result" I know that anonymous type can be used to perform this, but how to put them to dynamic expression? here is my code:

string item = "Name";
string item2 = "Test_Result";
Type studentType = typeof(Student);

ParameterExpression itemParam = Expression.Parameter(studentType, item);
MemberInfo itemProperty = studentType.GetProperty(item);

MemberExpression valueInItemField = 
    Expression.MakeMemberAccess(itemParam, itemProperty);

Expression<Func<Student, string>> selectExpression =
    Expression<Func<Student, string>>
        .Lambda<Func<Student, string>>(valueInItemField, itemParam);

IEnumerable<string> currentItemFields = 
    DeserializedStudents.Select(selectExpression.Compile());
1
  • Re your most recent repeat of the same; please clarify what is still unclear after the answers here and on your earlier question. If you tell us what is unclear, we can probably clarify. Commented Jan 8, 2013 at 12:23

1 Answer 1

30

I'm assuming that the "Name" and "Test_Result" here are flexible and cannot be hard-coded.

Anonymous types are fully defined regular classes; the only interesting thing about them is that the compiler provides the details instead of you.

I would suggest that the way to handle this scenario would be to use Tuple.Create to create an IEnumerable<Tuple<string,string>> and refer to them as Item1, Item2 (the names from Tuple<,>. The other option would be to use something like ExpandoObject, and then use either the IDictionary<string,object> API, or the dynamic API, to get the values back out.

For example:

string item1 = "Name";
string item2 = "Test_Result";
Type studentType = typeof(Student);

var itemParam = Expression.Parameter(studentType, "x");
var member1 = Expression.PropertyOrField(itemParam, item1);
var member2 = Expression.PropertyOrField(itemParam, item2);
var selector = Expression.Call(typeof(Tuple), "Create",
    new[] { member1.Type, member2.Type }, member1, member2);
var lambda = Expression.Lambda<Func<Student, Tuple<string,string>>>(
    selector, itemParam);

var currentItemFields = students.Select(lambda.Compile());

Here's the same projecting into a custom type with members name and result:

class ProjectedData
{
    public string name { get; set; }
    public string result { get; set; }
}

...

string item1 = "Name";
string item2 = "Test_Result";
Type studentType = typeof(Student);

var itemParam = Expression.Parameter(studentType, "x");
var member1 = Expression.PropertyOrField(itemParam, item1);
var member2 = Expression.PropertyOrField(itemParam, item2);
var selector = Expression.MemberInit(Expression.New(typeof(ProjectedData)),
    Expression.Bind(typeof(ProjectedData).GetMember("name").Single(), member1),
    Expression.Bind(typeof(ProjectedData).GetMember("result").Single(), member2)
);
var lambda = Expression.Lambda<Func<Student, ProjectedData>>(
    selector, itemParam);

var currentItemFields = students.Select(lambda.Compile());

Or for the approach using a dictionary:

string[] fields = {"Name", "Test_Result"};
Type studentType = typeof(Student);

var itemParam = Expression.Parameter(studentType, "x");

var addMethod = typeof(Dictionary<string, object>).GetMethod(
    "Add", new[] { typeof(string), typeof(object) });
var selector = Expression.ListInit(
        Expression.New(typeof(Dictionary<string,object>)),
        fields.Select(field => Expression.ElementInit(addMethod,
            Expression.Constant(field),
            Expression.Convert(
                Expression.PropertyOrField(itemParam, field),
                typeof(object)
            )
        )));
var lambda = Expression.Lambda<Func<Student, Dictionary<string,object>>>(
    selector, itemParam);

var currentItemFields = students.Select(lambda.Compile());
Sign up to request clarification or add additional context in comments.

7 Comments

i mean i would like to change var students = DeserializedStudents.Select(cust => new { name = cust.Name, result = cust.Test_Result }); to dynamic expression
@Yarik you would have to define your own type is the point; if you are happy to define some type somewhere with members name and result, then fine: just create a class. I can add an example of that in a moment (done).
Could you tell me please one more thing? What should I do if don't know how many items i will need to select. I mean one time it can be two items, next time it can be four items and so on. Should i create some list with that items or what?
@Yarik personally I would populate a dictionary with the values. Do you want an example?
@Yarik added at the bottom
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.