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

时间:2016-06-26 22:24:25

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

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

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中以某种方式重建它?

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

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

我试过让DTO从实体上继承,但这似乎不起作用?

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

2 个答案:

答案 0 :(得分:2)

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

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

最后要注意的是,如果你 在ORM层附近工作,实体框架非常聪明,你可能会走很长的路而不试图翻译你的Func&lt; dto,bool&gt;到一个Func&lt; entity,bool&gt;。例如,在下面的代码中,访问&#34; context.Products&#34;返回&#34; DbSet&#34;并且调用Select on就返回一个IQueryable并调用Where 返回IQueryable。实体框架会将的所有转换为单个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();

..那么你可能对你已经拥有的Func很好。我认为你已经有了某种映射函数来从EF实体到DTO(但如果没有,那么你可能想看看AutoMapper来帮助你 - 它支持&#34;投影&#34;,这些是基本上是IQueryable地图)。

答案 1 :(得分:0)

我将把这个作为答案。谢谢丹的快速回答。看看你在说什么我可以写一个查询/过滤器类。例如,请使用以下代码:

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

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

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

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