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

标签 c# linq entity-framework lambda

我目前正在使用在线提供的众多存储库模式之一来通过 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 实例(包括映射所有导航属性),还是返回 dynamicobject从您的存储库中。这两种选择都不是很令人愉快。映射将包括大量的反射,这不会很快。通过返回动态类型,您将失去所有类型安全性。我想你已经看到这个问题了。

也就是说:您需要手动构建此表达式。基于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/

相关文章:

c# - 批处理文件 - 注册所有 Dll

linq - EF 4:从集合中删除子对象不会将其删除-为什么?

c# - Linq - 序列包含多个元素

c# - 调用数据访问层的通用方法

c# - 如何在异步方法中实现 InvokeRequired UI 模式?

c# - 通过 WCF 双工 channel 通过 SocketIO4NET 使用 Socket.IO 时应用程序关闭

c# - 我可以在 Linq-to-SQL 的 .OrderBy() 中使用 IComparer 吗?

entity-framework - Entity Framework 中多条记录的插入顺序

c# - 保存具有多个父项的 EF 实例

c# - 我应该从哪里开始处理 mvvmcross Gtk 支持?