How do I create and populate a dynamic object using a dynamically built lambda expression

◇◆丶佛笑我妖孽 提交于 2019-12-10 12:04:11

问题


I'm trying to create and populate a dynamic object from a dataset that is only known at run time. In the code below, I create my IEnumerable results from my dataset with some known fields (ID, Primary Data, DisplayOrder, IsActive) and one user-define field (Phone Number) which I won't know at design time, and therefore must be built dynamically. The code below works, but only because I've hard-coded the dynamic field Phone Number. How do I build the Lambda expression dynamically to handle those fields only known at runtime. I want the equivalent of

string fieldName = 'PhoneNumber = ';
string fieldSource = 'pd.tbList_DataText';
string expression = 'pd=>new { ID = pd.ID, PrimaryData=pd.PrimaryData';
expression += fieldName;
expression += FieldSource;
expression += '.Where(ld => ld.DataRowID == pd.ID && ld.ListColumnID == 1).Select(ld => ld.DataField).DefaultIfEmpty("").First()})';

var results = primaryData.Select(expression);

So how do I make that work? Thanks

         // Get base Data
         IEnumerable<tbList_Data> primaryData = await _tbList_DataRepository.GetAsync(ld => ld.ListID == listId && (ld.IsActive     == includeInactive ? ld.IsActive : true));

        // Get Final Results
        var results = primaryData.Select(pd => new {
            Id = pd.ID,
            PrimaryData = pd.PrimaryData,
            PhoneNumber = pd.tbList_DataText.Where(ld => ld.DataRowID == pd.ID && ld.ListColumnID == 1).Select(ld => ld.DataField).DefaultIfEmpty("").First()
        });

回答1:


I see several options.

1) Tuple

var results = primaryData.Select(pd => new {
    Id = pd.ID,
    PrimaryData = pd.PrimaryData,
    Extra = Tuple.Create("PhoneNumber", pd.tbList_DataText.Where(ld => ld.DataRowID == pd.ID && ld.ListColumnID == 1).Select(ld => ld.DataField).DefaultIfEmpty("").First())
});

// How to access:
foreach (var result in results)
{
    Console.WriteLine(result.Id);
    Console.WriteLine(result.PrimaryData);
    Console.WriteLine(result.Extra.Item1);
    Console.WriteLine(result.Extra.Item2);
}

2) Dictionary

// Using C# 6 notation
var results = primaryData.Select(pd => new Dictionary<string, object>{
    ["Id"] = pd.ID,
    ["PrimaryData"] = pd.PrimaryData,
    ["PhoneNumber"] = pd.tbList_DataText.Where(ld => ld.DataRowID == pd.ID && ld.ListColumnID == 1).Select(ld => ld.DataField).DefaultIfEmpty("").First()
});

// Using C# 5 notation
var results = primaryData.Select(pd => new Dictionary<string, object>{
    {"Id", pd.ID},
    {"PrimaryData", pd.PrimaryData},
    {"PhoneNumber", pd.tbList_DataText.Where(ld => ld.DataRowID == pd.ID && ld.ListColumnID == 1).Select(ld => ld.DataField).DefaultIfEmpty("").First()}
});

// How to access:
foreach(var result in results)
{
    Console.WriteLine(result["Id"]);
    Console.WriteLine(result["PrimaryData"]);
    Console.WriteLine(result["PhoneNumber"]);
}

3) Dynamic

var results = primaryData.Select(pd => {
    dynamic result = new System.Dynamic.ExpandoObject();
    result.Id = pd.ID;
    result.PrimaryData = pd.PrimaryData;

    // Choose one of the following. Since you only "PhoneNumber" at runtime, probably the second one.
    result.PhoneNumber = pd.tbList_DataText.Where(ld => ld.DataRowID == pd.ID && ld.ListColumnID == 1).Select(ld => ld.DataField).DefaultIfEmpty("").First();
    ((IDictionary<string, object>)result).Add("PhoneNumber", pd.tbList_DataText.Where(ld => ld.DataRowID == pd.ID && ld.ListColumnID == 1).Select(ld => ld.DataField).DefaultIfEmpty("").First());

    return result;
});

// How to access:
foreach(var result in results)
{
    Console.WriteLine(result.Id);
    Console.WriteLine(result.PrimaryData);

    // Both work, independently how you created them
    Console.WriteLine(result.PhoneNumber);
    Console.WriteLine(((IDictionary<string, object>)result)["PhoneNumber"]);
}

EDIT: just realized from question that the field source should be dynamic as well. So, in the above code, replace any occurrence of pb.tbList_DataText by:

((IEnumerable<X>)pb.GetType().GetField("tbList_DataText").GetValue(pb))

Where X should be the type of ld. But carefull! This cast can potentially fail.

Also, if you want a property instead of a field, just use GetProperty instead of GetField.



来源:https://stackoverflow.com/questions/43025094/how-do-i-create-and-populate-a-dynamic-object-using-a-dynamically-built-lambda-e

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!