【问题标题】:Passing a GetWhere query (Func<entityDTO,bool>) to a data layer method which needs a (Func<entity,bool>) parameter to work将 GetWhere 查询 (Func<entityDTO,bool>) 传递给需要 (Func<entity,bool>) 参数才能工作的数据层方法
【发布时间】:2016-06-26 22:24:24
【问题描述】:

我在使用实体框架的数据访问类中有以下方法:

public static IEnumerable<entityType> GetWhere(Func<entityType, bool> wherePredicate)
{
    using (DataEntities db = new DataEntities())
    {
        var query = (wherePredicate != null)
            ? db.Set<entityType>().Where(wherePredicate).ToList()
            : db.Set<entityType>().ToList();                    
        return query;
    }
}

当我在所有层中使用实体时,这工作正常......但是我正在尝试使用 DTO 类,我想做如下的事情:

public static IEnumerable<EntityTypeDTO> GetWhere(Func<EntityTypeDTO, bool> wherePredicate)
{
    //call a method here which will convert Func<EntityTypeDTO,bool> to 
    // Func<EntityType,bool>

    using (DataEntities db = new DataEntities())
    {
        var query = new List<EntityType>();
        if (wherePredicate == null)
        {
            query = db.Set<EntityType>().ToList();
        }
        else
        {   
            query = (wherePredicate != null)
                ? db.Set<EntityType>().Where(wherePredicate).AsQueryable<EntityType>().ToList()
                : db.Set<EntityType>().ToList();
        }
        List<EntityTypeDTO> result = new List<EntityTypeDTO>();
        foreach(EntityType item in query)
        {
            result.Add(item.ToDTO());
        }

        return result;
    }
}

基本上我想要一种将 Func 转换为 Func 的方法。

我认为我必须将 Func 分解为表达式树,然后以某种方式在 entityType 中重建它?

我想这样做是为了让表示层只传递表达式查询?

我是否遗漏了一些基本的东西,或者是否有更简单的设计模式可以在不知道查询细节的情况下将查询从 DTO 传递到数据访问类?

我尝试让 DTO 从似乎也不起作用的实体继承?

如果我缺少更好的设计模式,我会喜欢一个指针,我可以从那里进行调查......

【问题讨论】:

    标签: c# entity-framework n-tier-architecture data-transfer-objects


    【解决方案1】:

    首先我建议你在Entity Framework前面放一个自己的查询层,而不是允许传入任意Func,因为将来很容易传递Entity Framework无法翻译的Func到 SQL 语句中(它只能翻译 一些 表达式 - 基础很好,但如果您的表达式调用 C# 方法,例如,实体框架可能会失败)。

    因此,您的搜索层可以包含您构建为条件的类(例如,“ContainsName”搜索类或“ProductHasId”类),然后将其转换为搜索层中的表达式。这将您的应用程序与 ORM 完全分开,这意味着 ORM 详细信息(如实体或诸如 Funcs 可以翻译和不能翻译的限制)不会泄漏。有很多关于这种安排的文章。

    最后一点,不过,如果您正在在 ORM 层附近工作,那么实体框架非常聪明,您可能会在不尝试翻译 Func 的情况下获得很长的路要走到 Func。例如,在下面的代码中,访问“context.Products”会返回一个“DbSet”,在其上调用 Select 会返回一个 IQueryable,而在 also 上调用 Where 会返回一个 IQueryable。 Entity Framework 会将其所有转换为单个 SQL 语句,因此它不会将所有其他产品拉入内存,然后过滤该内存集上的 ID,它实际上会即使过滤器在投影类型(在您的情况下相当于 DTO)而不是实体框架实体上运行,也可以在 SQL 中执行过滤 -

    var results = context.Products
        .Select(p => new { ID = p.ProductID, Name = p.ProductName })
        .Where(p => p.ID < 10)
        .ToList();
    

    执行的SQL是:

    SELECT 
        [Extent1].[ProductID] AS [ProductID], 
        [Extent1].[ProductName] AS [ProductName]
    FROM [dbo].[Products] AS [Extent1]
    WHERE [Extent1].[ProductID] < 10
    

    所以,如果你改变你的代码得到类似的东西..

    return context.Products
        .Map<Product, ProductDTO()>()
        .Where(productDtoWherePredicate)
        .ToList();
    

    .. 那么你可能对你已经拥有的 Funcs 没问题。我假设您已经有某种映射函数可以从 EF 实体获取到 DTO(但如果没有,那么您可能希望查看 AutoMapper 来帮助您 - 它支持“投影”,基本上是 IQueryable 映射)。

    【讨论】:

      【解决方案2】:

      我将把这个作为答案。感谢 Dan 的快速回答。看看你在说什么,我可以编写一组查询/过滤器类。比如下面的代码:

      GetProducts().GetProductsInCategory().GetProductsWithinPriceRange(minPrice, maxPrice);
      

      此代码运行如下:Get Products 将获取表中的所有产品,其余函数将过滤结果。如果所有查询都像这样运行,它可能会给数据访问层/数据库服务器连接带来很大的负担……不确定。

      我将研究的另一个替代方案是: 如果每个函数都创建一个 Linq 表达式,我可以像这样组合它们:How do I combine multiple linq queries into one results set? 这可能允许我以一种可以从数据库返回过滤结果集的方式执行此操作。

      无论哪种方式,我都将其标记为已回答。当我有更多细节时,我会更新。

      【讨论】:

      • 如果 GetProducts 返回 IQueryable 而不是 IEnumerable 并且 GetProductsInCategory 和 GetProductsWithinPriceRange 都采用并返回 IQueryable 实例,那么实体框架将能够完成所有这些工作并转换将其转换为单个 SQL 语句,这意味着过滤全部在数据库中完成,而不是在访问层的内存中。当您过滤 IEnumerable 时,您是在内存中进行的,但只要您在处理 IQueryable ,就可以将工作卸载到数据库中。
      • 对于您的 B 计划,在组合您的标准函数时会有一些复杂性 - 您希望每个函数都是 Expression> 而不仅仅是 Func 所以EF 可以将它们转换为 SQL 子句(并且避免必须在内存中进行所有过滤,这可能需要从数据库中提取比严格必要的更多的数据)。我在另一个问题上留下的答案中有示例代码应该有帮助:stackoverflow.com/a/37907316/3813189
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-07-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多