我目前正在使用在线提供的众多存储库模式之一来通过 EF6 执行 CRUD 操作。我很高兴按原样使用它,但最近我收到了一些遗留项目,这些项目的数据库表的列数非常多。我想要一种方法,通过设计一种仅选择列的子集的方法,使我的应用程序以及 future 的应用程序更加流畅。
当前方法。
public virtual TEntity Get(Expression<Func<TEntity, bool>> where,
params Expression<Func<TEntity, object>>[] navigationProperties)
{
TEntity item = null;
IQueryable<TEntity> dbQuery = this.Context.Set<TEntity>();
//Apply eager loading
foreach (Expression<Func<TEntity, object>> navigationProperty in navigationProperties)
dbQuery = dbQuery.Include<TEntity, object>(navigationProperty);
item = dbQuery
.AsNoTracking() //Don't track any changes for the selected item
.FirstOrDefault(where); //Apply where clause
return item;
}
我想增强该方法以仅检索我需要的列,但仍返回 TEntity。 我确实知道我必须在 '.AsNoTracking()' 之后注入(inject) Select ,但我不确定如何传递属性,因为我只是从表达式树开始。 本质上我希望能够做到这一点。
public class Employee
{
public int EmployeeId { get;set; }
public string EmployeeRole { get;set; }
public string EmployeeFirstName { get;set; }
public string EmployeeLastName { get;set; }
public string DOB { get;set; }
...
}
Employee employee = EmployeeRepository.Get(where: e => e.EmployeeRole == "Developer",
columns: x => x.EmployeeFirstName, x => x.EmployeeLastName,
navigationProperties: null);
Where columns 是指定要添加到 Select 子句的列的表达式列表。 任何帮助,将不胜感激。 提前致谢...
更新。
我最终使用 DTO 来进行必要的查询和提取,因为我找不到一种优雅的方法来通用地执行它。我的一位同事开发了一个解决方案,但它使存储库变得过于复杂,并且将来很难管理。 因此,我创建了一个 StaffBasicInformation 类来保存我经常使用的列的子集。如果将来需要的话,我还为其创建了一个界面。下面的代码示例显示了 DTO 检索数据的最终实现。
public virtual IStaffBasicInformation GetStaffBasicInformation<TEntity2>(Expression<Func<TEntity2, bool>> where)
where TEntity2 : ActiveStaffMember
{
TEntity2 item = null;
StaffBasicInformation resultItem = null;
IQueryable<TEntity2> dbQuery = this.Context.Set<TEntity2>();
resultItem =
dbQuery.Where(where)
.Select(x => new StaffBasicInformation
{
GivenName = x.GivenName,
Department = x.Department,
Description = x.Description,
DisplayName = x.DisplayName,
Gender = x.Gender,
IID = x.IID,
Mail = x.Mail,
Title = x.Title,
ID = x.Id
})
.FirstOrDefault();
return resultItem;
}
最佳答案
完成投影后,您的返回值将不再是 TEntity
类型,它将是匿名类型。您必须决定是否要将此匿名类型映射到 TEntity
实例(包括映射所有导航属性),还是返回 dynamic
或 object
从您的存储库中。这两种选择都不是很令人愉快。映射将包括大量的反射,这不会很快。通过返回动态类型,您将失去所有类型安全性。我想你已经看到这个问题了。
也就是说:您需要手动构建此表达式。基于this answer您可以修改
public static IQueryable SelectDynamic(this IQueryable source, IEnumerable<string> fieldNames)
到
public static IQueryable SelectDynamic(this IQueryable source, IEnumerable<Expression> fieldNames)
结束从表达式中提取属性名称。我建议使用 ExpressionVisitor
来实现此目的,但您也可以使用 this answer 中的代码。
对于映射,您可以编译表达式并使用返回的 Func
从匿名类型中检索值。之后,您需要使用表达式并再次使用 ExpressionVisitor
查找所选属性的托管类型。然后,您需要通过 Activator.CreateType()
创建一个 TEntity
类型的对象,以及每种托管类型的对象。使用表达式中的属性名称将 Func(AnonymousType)
中的值分配给创建的托管类型对象。之后,您必须确定 TEntity
和托管类型之间的关系并构建它。
明天我会尝试针对这种情况发布一些代码,尽管我很确定有更好更快的方法。
关于c# - 将对象属性的 lambda 表达式传递给方法以选择 EF 中的列,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28001352/