有没有办法在没有数据源的情况下创建LINQ查询作为变量?

时间:2011-12-30 19:46:56

标签: c# asp.net linq entity-framework-4 linq-to-entities

序言
我的核心问题与此问题非常相似:How can I write a clean Repository without exposing IQueryable to the rest of my application?一直未得到答复。我希望如果我以不同的方式解决问题,并提出一个稍微不同的问题,我可能会得到一个结果。我将重复这个问题的一些内容,以避免要求读者阅读上下文。

问题:
我正在使用POCO实体和实体框架4.我试图在应用层允许对实体集进行复杂的临时过滤,同时试图避免将IQueryable<T>暴露在我的存储库边界之外。这让我有些困难。

  • 我不想在存储库上创建一个带有大量参数的大规模过滤方法,例如:

    IEnumerable GetFilteredCustomers(string nameFilter, string addressFilter, bool isActive, int customerId, ...)
    

    这不仅非常麻烦,而且看起来非常难看,特别是如果它主要是一堆零点等等。它也不像我想的那样可维护。

  • 我不想在存储库上创建大量的过滤方法,例如:

    IEnumerable GetActiveCustomers()
    IEnumerable GetCustomersByName()
    

    这种方法存在许多问题,包括需要大量增加到n!的方法,其中n是可用过滤条件的数量,如果我希望能够以任意方式组合它们。 (即所有名为George的活跃客户)。也很难维护。

  • 我不想创建操纵IEnumerable<T>的可链接方法(Fluent Interface),因为最终涉及从数据库中带回一个巨大的结果集并将其过滤到内存中而不是可扩展的解决方案。

  • 我无法创建操作IQueryable<T>的Fluent界面,因为正如我已经说过的那样,我不希望将IQueryable<T>暴露在存储库之外。

  • 我想避免简单地通过传入一个充满参数而不是大参数列表的对象来重复单个大规模过滤方法,尽管此时这可能是最难看的解决方案。

思路:
最终,我认为一个理想的解决方案是发现一些方法来创建一个不知道源的完整查询,并将其存储为参数。然后我可以将其传递到知道源的存储库中,并将查询应用于源并返回结果。

澄清;与上面提到的简单地创建参数对象相比,我想使用原始LINQ查询,但以某种方式将它们存储在变量中,并稍后将它们应用于数据源。我怀疑返回类型必须提前知道,但我完全可以定义并事先知道它。

要从另一个角度查看,请考虑以下事项:

IQueryable<Customer> filteredCustomers = customerRepository.GetAll()
    .Where(c => c.FirstName == "Dave")
    .Where(c => c.IsActive == true)
    .Where(c => c.HasAddress == true)
    ;

我想将三个Where子句打包为一个查询对象,与customerRepository.GetAll()完全分开,将其作为参数传递并稍后应用。

2 个答案:

答案 0 :(得分:10)

不确定。您可以编写如下方法:

public Expression<Func<Customer, bool>> GetDave()
{
    return c => c.FirstName == "Dave"
             && c.IsActive
             && c.HasAddress;
}

...和存储库方法如:

public IEnumerable<Customer> GetOneGuy(Expression<Func<Customer, bool>> criteria)
{
    return Context.Customers.Where(criteria);
}

...并致电:

var dave = Repository.GetOneGuy(this.GetDave()).Single();

答案 1 :(得分:1)

如果您只想以可重用的方式捕获这些类型的查询,可以在IQueryable<Customer>(或任何您想要的POCO)上将其实现为扩展方法。类似的东西:

public static class ExtnensionsForIQueryableCustomer
{
    public static IEnumerable<Customer> WhereActiveWithAddressAndNamed (this IQueryable<Customer> queryable, string name)
    {
        return queryable.Where (c => c.FirstName == name)
                        .Where (c => c.IsActive)
                        .Where (c => c.HasAddress);
    }
}

然后您可以使用它:

customerRepository.GetAll ().WhereActiveWithAddressAndNamed ("Dave");