2

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

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 从似乎也不起作用的实体继承?

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

4

2 回答 2

2

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

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

最后要注意的是,如果您ORM 层附近工作,实体框架非常聪明,您可能会获得很长的路要走,而无需尝试将 Func<dto, bool> 转换为 Func<entity, bool>。例如,在下面的代码中,访问“context.Products”会返回一个“DbSet”,对其调用 Select 会返回一个 IQueryable,调用 Where也会返回一个 IQueryable。Entity Framework 会将所有这些转换为单个 SQL 语句,因此它不会将所有其他产品拉入内存,然后过滤该内存集上的 ID,

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 映射)。

于 2016-06-27T07:49:08.403 回答
0

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

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

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

或者

我将研究的另一种方法是:如果每个函数都创建一个 Linq 表达式,我可以像这样组合它们:如何将多个 linq 查询组合成一个结果集? 这可能允许我以可以从数据库返回过滤结果集的方式执行此操作。

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

于 2016-06-27T09:55:43.977 回答