虽然这个问题对于8年后的OP没有用处,但是由于这个问题有很多点击量,因此我认为为其他人提供正确答案可能会有所帮助。
如果您使用Entity Framework,应该使用Linq projection(Select()
),因为这会在数据库端生成正确且有效的查询,而不会拉取整个实体。
使用Linq Select()
时,通常需要提供lambda表达式,因此在字符串中包含列/属性名称是主要难点。
最简单的解决方法是使用Dynamic LINQ(EntityFramework.DynamicLinq Nuget软件包)。该软件包提供了原始Linq方法的替代方法,它们将字符串作为参数,并将这些字符串转换为适当的表达式。
例如:
async Task<int> GetIntColumn(int entityId, string intColumnName)
{
return await TableRepository.Entities
.Where(x => x.Id == entityId)
.Select(intColumnName)
.Cast<int>()
.SingleAsync();
}
我还将此转换为async
调用,因为现在所有的数据库调用都应该异步执行。当您调用此方法时,必须await
它以获取结果(即:var res = await GetIntColumn(...);
)。
泛型变体
也许将其更改为IQueryable
的扩展方法,并将列/属性类型更改为泛型类型参数会更有用,这样您可以使用任何列/属性:
(前提是您所有实体都具有指定Id
属性的公共接口。)
public static async Task<TColumn> GetColumn<TEntity, TColumn>(this IQueryable<TEntity> queryable, int entityId, string columnName)
where TEntity : IEntity
{
return await queryable
.Where(x => x.Id == entityId)
.Select(columnName)
.Cast<TColumn>()
.SingleAsync();
}
这样称为:var result = await TableRepository.Entities.GetColumn<Entity, int>(id, columnName);
泛型变量可以接受列的列表
您可以进一步扩展它以支持动态选择多个列:
public static async Task<dynamic> GetColumns<TEntity>(this IQueryable<TEntity> queryable, int entityId, params string[] columnNames)
where TEntity : IEntity
{
return await queryable
.Where(x => x.Id == entityId)
.Select($"new({string.Join(", ", columnNames)})")
.Cast<dynamic>()
.SingleAsync();
}
这被称为这样:var result = await TableRepository.Entities.GetColumns(id, columnName1, columnName2, ...);
。
由于返回类型及其成员在编译时未知,因此我们必须在这里返回dynamic
。这使得与结果一起工作变得困难,但如果您只想将其序列化并发送回客户端,那么对于该目的来说就可以了。