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

发布于 2024-12-23 12:59:48 字数 1845 浏览 3 评论 0原文

序言:
我的核心问题与此非常相似: 如何编写一个干净的存储库而不将 IQueryable 暴露给我的应用程序的其余部分? 这仍然没有答案。我希望如果我以不同的方式处理这个问题,并提出一个稍微不同的问题,我可能会得到结果。我将重复另一个问题中的一些内容,以避免要求读者阅读它以了解上下文。

问题:
我正在使用 POCO 实体和实体框架 4。我试图允许在应用程序层对实体集进行复杂的临时过滤,同时尝试避免暴露 IQueryable 过去我的存储库边界。这给我留下了一些麻烦。

  • 我不想在存储库上创建一个需要大量参数的大型过滤方法,例如:

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

    这不仅使用起来极其麻烦,而且看起来也非常难看,特别是如果它主要是一堆空值等。它也不像我希望的那样易于维护。

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

    IEnumerable GetActiveCustomers()
    IEnumerable GetCustomersByName()
    

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

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

  • 我无法创建操作 IQueryable 的 Fluent Interface,因为正如我已经说过的,我不想公开 IQueryable >过去的存储库。

  • 通过传入一个充满参数的对象而不是一个大的参数列表来简单地重新散列单个大规模过滤器方法,尽管目前这可能是最不难看的解决方案。

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

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

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

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

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

Preamble:
My core question is very similar to this one: How can I write a clean Repository without exposing IQueryable to the rest of my application? which has remained unanswered. I am hoping that if I approach the problem in a different way, and ask a slightly different question, I may get a result. I will repeat some of the content from this other question to avoid requiring readers to read it for context.

Problem:
I'm working with POCO entities, and Entity Framework 4. I am trying to allow for sophisticated ad-hoc filtering of entity sets at the application layer, while simultaneously trying to avoid exposing IQueryable<T> past my repository boundary. This is leaving me with some complications.

  • I do not want to create a single massive filter method on the repository that takes a huge list of parameters, such as:

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

    Not only is this extremely cumbersome to use, but it's super ugly to look at, especially if it's mostly a bunch of nulls, etc. It's also not as maintainable as I would like.

  • I do not want to create a huge set of filter methods on the repository, such as:

    IEnumerable GetActiveCustomers()
    IEnumerable GetCustomersByName()
    

    There are a number of problems with this approach, including needing a huge list of methods which grows to n! where n is the number of available filter conditions if I want to be able to combine them in arbitrary ways. (i.e. all active customers with name George). Also highly difficult to maintain.

  • I do not want to create chainable methods (Fluent Interface) that manipulate IEnumerable<T>, because ultimately that involves bringing back a huge result set from the database and filtering it down in-memory which is not a scalable solution.

  • I can't create a Fluent Interface that manipulates IQueryable<T> because as I've already said, I don't want to expose the IQueryable<T> past the repositories.

  • I'd like to avoid simply rehashing the single massive filter method by passing in an object full of parameters instead of a large parameter list, although at this point this might be the least ugly solution.

Ideas:
Ultimately, I think an ideal solution would be discovering some way to create a full query that doesn't know the source, and store it as a parameter. I could then pass that into the repository, where the source is known, and apply the query to the source and return the results.

To clarify; in contrast to simply creating an object of parameters as mentioned above, I'd like to use the raw LINQ queries, but store them in a variable somehow, and apply them to a data source later. I suspect the return type would have to be known ahead of time, but I'm perfectly fine with defining that and having it known in advance.

To view it from yet another perspective, consider the following:

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

I want to package up the three Where clauses as a query object, completely separate from the customerRepository.GetAll(), pass it around as a parameter and apply it later.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

七秒鱼° 2024-12-30 12:59:48

当然。您可以编写如下方法:

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();

Sure. You can write a method like:

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

...and repository methods like:

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

...and call:

var dave = Repository.GetOneGuy(this.GetDave()).Single();
晌融 2024-12-30 12:59:48

如果您只想以可重用的方式捕获这些类型的查询,您可以将其实现为 IQueryable (或您想要的任何 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");

If you just want to capture these kinds of queries in a re-usable way, you might implement this as extension methods on IQueryable<Customer> (or whatever POCO you want). Something like:

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);
    }
}

You could then consume this like:

customerRepository.GetAll ().WhereActiveWithAddressAndNamed ("Dave");
酒中人 2024-12-30 12:59:48

您可以构建一个表达式并稍后使用它

var isg = Guid.TryParse(id, out gLT);
Expression<Func<YourObjectResultType, bool>> query = w => w.Id == id;

if (isg) {
    query = w => w.Token == gLT;
}

var result = _context.TblYourTable.Where(query);

You can build an expression and use it later

var isg = Guid.TryParse(id, out gLT);
Expression<Func<YourObjectResultType, bool>> query = w => w.Id == id;

if (isg) {
    query = w => w.Token == gLT;
}

var result = _context.TblYourTable.Where(query);
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文