将 GetWhere 查询 (Func<entityDTO,bool>) 传递给需要 (Func<entity,bool>) 参数才能工作的数据层方法

Passing a GetWhere query (Func<entityDTO,bool>) to a data layer method which needs a (Func<entity,bool>) parameter to work

我在使用 entity framework 的数据访问 class 中有以下方法:

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 class,并且我想执行如下操作:

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 传递到数据访问class?

我试过使 DTO 继承自似乎也不起作用的实体?

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

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

所以您的搜索层可以有 class 您建立的标准(例如 "ContainsName" 搜索 class 或 "ProductHasId" class ) 然后在您的搜索层中转换为表达式。这将您的应用程序与 ORM 完全分开,这意味着 ORM 详细信息(例如实体或 Funcs 可以翻译和不能翻译的限制)不会泄露。有很多关于这种安排的文章。

最后要注意的是,如果您 在 ORM 层附近工作,Entity Framework 非常聪明,您可能会在不尝试的情况下走很长一段路将您的 Func 转换为 Func。例如,在下面的代码中,访问 "context.Products" returns a "DbSet" 并在其上调用 Select returns IQueryable 并在其上调用 Where returns 一个IQueryable。 Entity Framework 会将其中的 all 转换为单个 SQL 语句,因此 不会 将所有其他 Products 拉入内存然后过滤该内存集上的 ID,它实际上会在 SQL 中执行过滤,即使过滤器在投影类型(相当于您的情况下的 DTO)而不是 Entity Framework实体-

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 来帮助您 - 它支持 "projections",它们基本上是 IQueryable 映射).

我将把它作为 answer.Thanks 提交给 Dan,以便快速回答。看你说的我可以写query/filter套类套。例如,采用以下代码:

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

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

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

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