在linq查询中以匿名类型设置动态对象的所有属性

Posted

技术标签:

【中文标题】在linq查询中以匿名类型设置动态对象的所有属性【英文标题】:Setting all properties of dynamic object in anonymous type in linq query 【发布时间】:2013-10-11 15:03:22 【问题描述】:

我需要将动态对象(即List<DynamicRow>)转换为匿名类型对象列表,但我需要在运行时使用 linq 或一些将动态对象的所有属性创建为匿名类型对象其他方式。

这可能吗?如果是这样,我该怎么做?

这个动态对象几乎是一个网格类,它包含一个列列表,即List<String>,以及行,即List<DynamicRow>

我可以使用列名(即Order[<columnName>] -> Order["OrderNumber"])访问 DynamicRow 对象(例如 Order)中保存的值,所以我希望能够使用 Linq 做同样的事情。

有没有办法循环遍历列列表并为每一行创建一个匿名对象类型,其中每个属性都基于列名设置。

虽然我知道下面的代码不起作用,但它可能会使我想要实现的目标更加清晰:

所以不要这样:

var itemsList = from p in customerOrderCsvReader
select new
                            
 Item = p.ElementAt(0).Value,
 Description = p.ElementAt(1).Value,
 Comment = p.ElementAt(2).Value
;

我想要这样的东西,因为我不会提前知道项目、描述和评论等属性名称。我知道动态对象中的内容的唯一方法是使用列名。

var itemsList = from p in customerOrderCsvReader
select new

 foreach string columnName in customerOrderCsvReader.columnNames
 
  <columnName as property> = p.ElementAt(0).Value;
 
;

另一个问题,假设上述是可行的,有没有办法让我使用键访问 p 的属性,即 columnName 而不是 p.ElementAt。

更新

我可能应该更清楚。我正在尝试将其绑定到网格,但我也在尝试保持此通用性,以便它可以在任何平台上工作,而不仅仅是 winform。

如果我使用@jjchiw 提供的代码,在输出到我的输出窗口时会得到以下信息:

Count = 6
    [0]: System.Dynamic.ExpandoObject
    [1]: System.Dynamic.ExpandoObject
    [2]: System.Dynamic.ExpandoObject
    [3]: System.Dynamic.ExpandoObject
    [4]: System.Dynamic.ExpandoObject
    [5]: System.Dynamic.ExpandoObject

但是,如果我使用 linq 并提供一些虚拟属性名称并使用以下方式为其分配一些值:

var itemsList = from p in customerOrderCsvReader
                select new
                
                  CustomerId = p.ElementAt(0),
                  EmployerId = p.ElementAt(1),
                  Description = p.ElementAt(2)
                ;

我得到以下输出到我的输出窗口:

Count = 6
    [0]:  CustomerId = "GREAL", EmployerId = "6", Description = "1997-05-06T00:00:00" 
    [1]:  CustomerId = "HUNGC", EmployerId = "3", Description = "1997-05-06T00:00:00" 
    [2]:  CustomerId = "LAZYK", EmployerId = "8", Description = "1997-05-06T00:00:00" 
    [3]:  CustomerId = "LETSS", EmployerId = "1", Description = "1997-05-04T00:00:00" 
    [4]:  CustomerId = "WALLM", EmployerId = "5", Description = "1997-05-04T00:00:00" 
    [5]:  CustomerId = "TOTAL", EmployerId = "4", Description = "1997-05-06T00:00:00" 

将动态列表绑定到我的网格时,它不显示任何内容,但使用匿名类型列表时,从技术上讲,它现在包含一个“可识别”对象列表,我的网格会相应地显示数据。

是否可以强制动态列表表现得像匿名列表,以便我可以将动态列表绑定到我的网格但显示正确的结果。

【问题讨论】:

如果您只在运行时知道属性名称,您打算如何处理生成的匿名对象? 【参考方案1】:

创建一个 ExpandoObject 并转换为 IDictionary&lt;string, object&gt; 应该做你想做的事情,像这样

var columnNames = new List<string>"Foo", "Foo2";
var customerOrderCsvReader = new List<List<string>>new List<string>"Bar", "Bar2";


var list = new List<dynamic>();
foreach (var element in customerOrderCsvReader)

    var expando = new ExpandoObject();
    var temp = (IDictionary<string, object>) expando;
    int i = 0;
    foreach(string columnName in columnNames)
    
        temp[columnName] = element[i];
        i++;
    
    list.Add(expando);


//Print Bar
Console.WriteLine (list[0].Foo);
//Print Bar2
Console.WriteLine (list[0].Foo2);

//Print Bar
Console.WriteLine ((list[0] as IDictionary<string, object>)["Foo"]);
//Print Bar2
Console.WriteLine ((list[0] as IDictionary<string, object>)["Foo2"]);

编辑。

我找到了这个库ImpromptuInterface (nuget),我记得这个问题 也许不是您想要的,因为在此解决方案中您需要为 Datagrid 定义接口,并且您希望所有内容都是匿名的......无论如何填充 Datagrid 很有趣

public partial class Form1 : Form

    private Dictionary<string, Type> _columnTypes;

    public Form1()
    
        InitializeComponent();

        _columnTypes = new Dictionary<string, Type>();
        _columnTypes.Add("FooFoo2", typeof(IFoo));
    



    private void Form1_Load(object sender, EventArgs e)
    
        var columnNames = new List<string>  "Foo", "Foo2" ;
        var customerOrderCsvReader = new List<List<string>>  new List<string>  "Bar", "Bar2"  ;

        var type = _columnTypes[string.Join("", columnNames)];

        var type2 = typeof(DataSourceBuilder<>).MakeGenericType(type);
        dynamic dataBuilder = Activator.CreateInstance(type2);  

        var list = dataBuilder.GetDataSource(columnNames, customerOrderCsvReader);
        dataGridView1.DataSource = list;
    


public class DataSourceBuilder<T> where T : class, IDataSource

    public List<T> GetDataSource(List<string> columnNames, List<List<string>> customerOrderCsvReader)
    
        var list = new List<T>();
        foreach (var element in customerOrderCsvReader)
        
            dynamic expando = new ExpandoObject();
            var temp = (IDictionary<string, object>)expando;
            int i = 0;
            foreach (string columnName in columnNames)
            
                temp[columnName] = element[i];
                i++;
            
            var foo = Impromptu.ActLike<T>(temp);
            list.Add(foo);
        

        return list;
    


public interface IDataSource



public interface IFoo : IDataSource

    string Foo  get; set; 
    string Foo2  get; set; 

【讨论】:

我已经更新了我的问题。可以请您检查一下,看看是否可以实现? 我花了更长的时间才回复!! :) - 无论如何,感谢您花时间更新此内容!我将使用 IDictionary 选项!它到底是什么!最好保持简单,而不是引入另一个库。

以上是关于在linq查询中以匿名类型设置动态对象的所有属性的主要内容,如果未能解决你的问题,请参考以下文章

具有匿名类型和用户定义类型的 LINQ 选择查询

LINQ查询使用GROUP BY和Count(*)进入匿名类型

linq-to-sql 是不是处理动态查询?

Linq语句查询

LINQ学习

具有匿名类型的 C# LINQ 构建表达式