将对象属性的lambda表达式传递给方法以选择EF中的列

时间:2015-01-17 16:03:31

标签: c# linq entity-framework lambda

我目前正在使用在线提供的众多存储库模式之一来执行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;            
        }

1 个答案:

答案 0 :(得分:2)

完成投影后,您的返回值将不再是TEntity类型,它将是匿名类型。如果要将此匿名类型映射到TEntity的实例,包括映射所有navigationproperties,或从存储库返回dynamicobject,则必须决定。两种选择都不是很愉快。映射将包括大量的反射,这将不会非常快。通过返回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与托管类型之间的关系并进行构建。

我会尝试明天为这个场景发布一些代码,虽然我很确定有更好更快的方法。