使用DataContext从LINQ查询快速填充DataTable的最佳方法

8

我正在尝试运行一个linq查询,但我需要将结果作为datatable返回,因为我要使用它来存储来自同一viewstate对象中不同查询的记录。

下面的2个版本编译通过,但返回一个空集。确切的错误是"值不能为空。参数名:源"。(是的,我已经检查过有数据):

MyDatabaseDataContext db = new MyDatabaseDataContext(conn); 
IEnumerable<DataRow> queryProjects = 
    (from DataRow p in db.STREAM_PROJECTs.AsEnumerable()
    where p.Field<int>("STREAM_ID") == StreamID
    select new
    {
        PROJECT_ID = p.Field<int>("PROJECT_ID"),
        PROJECT_NAME = p.Field<string>("PROJECT_NAME")
    }) as IEnumerable<DataRow>;
DataTable results = queryProjects.CopyToDataTable<DataRow>();

...

//(from p in db.STREAM_PROJECTs.AsEnumerable()
//where p.STREAM_ID == StreamID
//select new
//{
//    p.PROJECT_NAME,
//    p.PROJECT_ID
//}) as IEnumerable<DataRow>;

这个帖子中的例子似乎在这种情况下也无法运行。
我猜我可以按照传统方式运行sql查询命令,但是linq不应该更快吗?
1个回答

20
您的问题是这样的:
as IEnumerable<DataRow>

`as`关键字执行的是安全转换,而不是转换,这可能会让你误以为它正在进行转换。`as`关键字在语义上与执行以下操作相同:
IEnumerable<DataRow> queryProjects = 
    (IEnumerable<DataRow>)(from DataRow p in db.STREAM_PROJECTs.AsEnumerable()
    where p.Field<int>("STREAM_ID") == StreamID
    select new
    {
        PROJECT_ID = p.Field<int>("PROJECT_ID"),
        PROJECT_NAME = p.Field<int>("PROJECT_NAME")
    });

除了使用as版本在将查询对象(一个IQueryable<T>,其中T是匿名类型)强制转换为IEnumerable<DataRow>时(它不是这个类型)不会抛出异常之外,没有其他方法可以将具体类型的可枚举对象(例如您示例中的匿名类型)转换为DataTable。编写一个方法并不太复杂,因为您基本上需要反射获取属性,然后迭代该集合并使用这些属性在DataTable中创建列。稍后我会发布一个示例。

放置在您正在使用的命名空间内的静态类中的以下内容应提供一个扩展方法,可以实现您想要的功能:

public static DataTable ToDataTable<T>(this IEnumerable<T> source)
{
    PropertyInfo[] properties = typeof(T).GetProperties();

    DataTable output = new DataTable();

    foreach(var prop in properties)
    {
        output.Columns.Add(prop.Name, prop.PropertyType);
    }

    foreach(var item in source)
    {
        DataRow row = output.NewRow();

        foreach(var prop in properties)
        {
            row[prop.Name] = prop.GetValue(item, null);
        }

        output.Rows.Add(row);
    }

    return output;
}

我明白了 - 所以它只是隐藏了无效的转换异常?那么我怎样将其放入数据表中呢? - JumpingJezza
1
@JumpingJezza:是的;在你知道对象可能不是你期望的类型并且不是“异常”情况的情况下,应该使用as。如果转换无效,则as关键字将计算为null,因此只能与引用类型和Nullable<T>一起使用。请参见我刚刚发布的编辑,其中包含一个示例扩展方法,可以将任何IEnumerable<T>转换为DataTable - Adam Robinson
太好了!而且我还发现了关于 as 的新知识 :) - JumpingJezza
我不得不加入一个检查 Nullable 类型的代码,因为如果类型是可空的,output.Columns.Add 调用将失败。 - PBMe_HikeIt

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接