2013-05-13 97 views
37

考慮,我們有這個類:LINQ:動態選擇

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; } 

} 

如何動態地選擇指定的列?像這樣:

var list = new List<Data>(); 

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

這是唯一的解決方案=>Dynamic LINQ
所選字段在編譯時未知。他們將在運行時指定

+0

是的,如果你的過濾器輸入是字符串 – 2013-05-13 07:39:39

+0

@Congong如何?任何代碼示例或鏈接都會很棒。 – Unforgiven 2013-05-13 07:47:31

+1

你想要結果類型是什麼? – 2013-05-13 07:50:12

回答

47

可以通過動態創建傳遞給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(); 
} 

拉姆達這樣做,那麼你可以使用它像這樣:

var result = list.Select(CreateNewStatement("Field1, Field2")); 
+1

雖然這適用於直接成員,但它不適用於嵌套值。例如:我有一個父對象,並且我想從父對象中選擇child.name。我正在解析輸入字符串的句點和嵌套調用getProperty。 – Dave 2013-11-18 21:06:55

+2

數據來自哪裏(Func )? – 2014-10-10 14:03:22

+7

我建議將它聲明爲'Func CreateNewStatement (string fields)',用'T'在函數內部替換'Data'的每個出現,並將其用作'var result = list.Select(CreateNewStatement (「Field1,字段2" ));'。這允許將它用於每個實體,而不僅僅用於「數據」。 – Matt 2014-11-03 15:31:38

-7
var result = from g in list.AsEnumerable() 
       select new {F1 = g.Field1,F2 = g.Field2}; 
+6

這不是動態的 – 2013-05-13 07:46:28

+0

@ThomasLevesque這裏有什麼動態的意思 – 2013-05-13 07:46:53

+3

這意味着選定的字段在編譯時是不知道的。它們將在運行時指定,例如使用字符串 – 2013-05-13 07:47:52

4

使用反射和表達式bulid可以做你所說的。 例子:

var list = new List<Data>(); 
//bulid a expression tree to create a paramter 
ParameterExpression param = Expression.Parameter(typeof(Data), "d"); 
//bulid expression tree:data.Field1 
Expression selector = Expression.Property(param,typeof(Data).GetProperty("Field1")); 
Expression pred = Expression.Lambda(selector, param); 
//bulid expression tree:Select(d=>d.Field1) 
Expression expr = Expression.Call(typeof(Queryable), "Select", 
    new Type[] { typeof(Data), typeof(string) }, 
    Expression.Constant(list.AsQueryable()), pred); 
//create dynamic query 
IQueryable<string> query = list.AsQueryable().Provider.CreateQuery<string>(expr); 
var result=query.ToList(); 
4

您必須使用反射來獲取並設置其名稱的屬性值。

var result = new List<Data>(); 
    var data = new Data(); 
    var type = data.GetType(); 
    var fieldName = "Something"; 

    for (var i = 0; i < list.Count; i++) 
    { 
     foreach (var property in data.GetType().GetProperties()) 
     { 
     if (property.Name == fieldName) 
     { 
      type.GetProperties().FirstOrDefault(n => n.Name == property.Name).SetValue(data, GetPropValue(list[i], property.Name), null); 
      result.Add(data); 
     } 
     } 
    } 

這裏是GetPropValue()方法

public static object GetPropValue(object src, string propName) 
{ 
    return src.GetType().GetProperty(propName).GetValue(src, null); 
} 
1

除了尼古拉斯巴特勒和馬特(類型輸入類是使用T)的評論暗示,我把一個提高尼古拉斯動態生成實體屬性的應答,功能二進制不需要發送field作爲參數。

對於使用添加類作爲打擊:

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(); 
    } 
} 

DynamicSelectGenerator方法得到實體T型,這種方法具有可選的輸入參數Fields,如果你想從實體slect專業派作爲一個字符串,如"Field1, Field2",如果你不發送任何內容methid,它返回所有實體的字段,你可以使用下面這個方法:

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(); 
      } 

(假設你有一個DbContext與名稱AppDbContext和上下文與名稱SampleEntity

+0

請在答案中添加一些用法示例。 – Matt 2017-07-21 08:57:27

+0

@Matt我編輯答案,並通過樣本 – 2017-07-21 18:45:12

+0

@Matt編輯答案並改進,如果有人可以通過此代碼實現對「選擇」方法的覆蓋 – 2017-07-21 18:48:13

0

我已經使用的另一種方法是一個嵌套三元運算符的實體:

string col = "Column3"; 
var query = table.Select(i => col == "Column1" ? i.Column1 : 
           col == "Column2" ? i.Column2 : 
           col == "Column3" ? i.Column3 : 
           col == "Column4" ? i.Column4 : 
           null); 

三元運算符要求每個字段具有相同的類型,所以您需要在任何非字符串列上調用.ToString()。