在使用Entity Framework4 (EF4) 及其ESQL查询语法时,执行查询后通常会得到一个DbDataRecord
对象集合。这种情况通常发生在查询返回一个匿名类型时。DbDataRecord
对象让想起了ADO.NET中的DbDataReader
,它是一个抽象项,可以包含任何内容。要获取数据,只需遍历DbDataReader
并按列名或索引获取数据。
本文使用的环境包括:
将展示3个示例,从简单到复杂,展示如何将DbDataRecord
转换为自定义的类型。
让先获取一些类型化数据,比如所有客户信息。使用
SELECT VALUE item FROM
类似于T-SQL中的SELECT * FROM
,但同时指定了结果的特定类型,本例中为Customer
。
using (AdventureWorksLT2008Entities ctx = new AdventureWorksLT2008Entities())
{
string query = string.Format("SELECT VALUE item FROM {0}.Customers AS item", ctx.DefaultContainerName);
ObjectQuery customersQuery = new ObjectQuery(query, ctx);
}
现在想要获取所有记录,但只包含FirstName
、LastName
和CompanyName
列。如果尝试使用之前的代码并仅更改查询,EF4会给出错误提示,因为无法将System.Data.Objects.MaterializedDataRecord
类型转换为Customer
类型。要解决这个问题,必须使用ObjectQuery<DbDataRecord>
。
using (AdventureWorksLT2008Entities ctx = new AdventureWorksLT2008Entities())
{
string query = string.Format("SELECT item.FirstName, item.LastName, item.CompanyName FROM {0}.Customers AS item", ctx.DefaultContainerName);
ObjectQuery customersQuery = new ObjectQuery(query, ctx);
}
现在有了所有数据作为匿名类型,而不是Customer
类型。这在仅需要显示/使用原始数据时很好,但如果想要将这些匿名类型列表转换为客户列表呢?如果更进一步,想要获取一个自定义类型的列表,而这个类型恰好具有与查询中检索的列同名的属性呢?没问题,只需使用这些扩展方法即可。
public static class AnonymousTypeConversion
{
public static T ConvertTo(this DbDataRecord record)
{
T item = Activator.CreateInstance();
for (int f = 0; f < record.FieldCount; f++)
{
PropertyInfo p = item.GetType().GetProperty(record.GetName(f));
if (p != null && p.PropertyType == record.GetFieldType(f))
{
p.SetValue(item, record.GetValue(f), null);
}
}
return item;
}
public static List ConvertTo(this List list)
{
List result = (List)Activator.CreateInstance>();
list.ForEach(rec =>
{
result.Add(rec.ConvertTo());
});
return result;
}
}
现在,让创建一个自定义的Customer
类型,它具有更少的属性,并添加一个名为FullName
的新属性,该属性仅连接FirstName
和LastName
。
public class TinyCustomer
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string CompanyName { get; set; }
public string FullName
{
get
{
return (string.IsNullOrEmpty(FirstName) ? string.Empty : FirstName) + " " + (string.IsNullOrEmpty(LastName) ? string.Empty : LastName);
}
}
}
让从匿名查询中获取TinyCustomer
的列表。
using (AdventureWorksLT2008Entities ctx = new AdventureWorksLT2008Entities())
{
string query = string.Format("SELECT item.FirstName, item.LastName, item.CompanyName FROM {0}.Customers AS item", ctx.DefaultContainerName);
ObjectQuery customersQuery = new ObjectQuery(query, ctx);
lblQueryString.Text = query;
var tinyCustomers = customersQuery.ToList().ConvertTo();
}
计划很快写一篇关于EF4/ESQL动态查询的文章,发现这特别有用。做了一个包含本文所有代码的示例项目,只需下载示例项目,在SQL Server实例上安装AdventureWorks数据库,并在web.config中配置连接字符串。