8年后对OP没有用,但是这个问题有很多观点,所以我认为对其他人有一个正确的答案可能会有所帮助。
如果您使用实体框架,您应该进行 Linq 投影 ( Select()
),因为这会导致在 db 端进行正确、高效的查询,而不是拉入整个实体。
但是,使用 Linq Select()
,您通常必须提供 lambda,因此将您的列/属性名称放在字符串中是这里的主要困难。
最简单的解决方案是使用动态 LINQ(EntityFramework.DynamicLinq Nuget 包)。这个包提供了原始 Linq 方法的替代方案,这些方法将字符串作为参数,并将这些字符串转换为适当的表达式。
例子:
async Task<int> GetIntColumn(int entityId, string intColumnName)
{
return await TableRepository.Entities
.Where(x => x.Id == entityId)
.Select(intColumnName) // Dynamic Linq projection
.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) // Dynamic Linq projection
.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
这里返回。这使得处理结果变得困难,但如果你想要的只是序列化它并将其发送回客户端,那么就可以了。