我目前正在使用在线提供的众多存储库模式之一来执行EF6的CRUD操作。我很高兴按原样使用它,但我最近交付了一些具有非常多列的数据库表的遗留项目。我希望通过设计仅选择列的子集的方法,使我的应用程序以及未来的应用程序更顺畅。
现行方法。
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()之后注入一个选择,但我不确定如何传递属性,因为我刚开始使用表达式树。 从本质上讲,我希望能够做到这一点。
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);
其中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;
}
答案 0 :(得分:2)
完成投影后,您的返回值将不再是TEntity
类型,它将是匿名类型。如果要将此匿名类型映射到TEntity
的实例,包括映射所有navigationproperties,或从存储库返回dynamic
或object
,则必须决定。两种选择都不是很愉快。映射将包括大量的反射,这将不会非常快。通过返回dynamic
类型,您可以放弃所有类型的安全性。你已经看到了这个问题。
说:你需要手动构建这个表达式。基于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
查找所选属性的托管类型。然后,您需要通过TEntity
创建类型为Activator.CreateType()
的对象,并为每种托管类型创建对象。使用表达式中的属性名称将Func(AnonymousType)
中的值分配给托管类型的已创建对象。之后,您必须确定TEntity
与托管类型之间的关系并进行构建。
我会尝试明天为这个场景发布一些代码,虽然我很确定有更好更快的方法。