2013-05-13 92 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()。