【问题标题】:Repository With OrderBy带有 OrderBy 的存储库
【发布时间】:2010-06-25 16:01:08
【问题描述】:

我正在尝试创建一个存储库类,该类具有基于“排序”参数对结果进行排序的方法。我需要将它作为参数传递,因为我试图非常严格地确保我的存储库不返回 IQueryable 并且只返回 List。问题是我不知道如何使它满足以下要求:

  1. 允许多列。
  2. 强类型化到返回的实体(没有字符串作为参数的列)。
  3. 能够将特定列设置为降序。

Repository 允许按顺序返回,这甚至是可能的还是没用?存储库是否应该只能执行 CRUD 操作?也许返回 IQueryable 是最好的选择?

【问题讨论】:

  • I'm trying to be very strict that my repository doesn't return IQueryable and only returns List. - 不要这样做。 IQueryable/IEnumerable 是你的朋友。 (查看google.com/search?q=deferred+execution

标签: c# asp.net-mvc linq


【解决方案1】:

也许你需要这样的东西:

public class Ordering<T>
{
    private readonly Func<IQueryable<T>, IOrderedQueryable<T>> transform;

    private Ordering(Func<IQueryable<T>, IOrderedQueryable<T>> transform)
    {
        this.transform = transform;
    }

    public static Ordering<T> Create<TKey>
        (Expression<Func<T, TKey>> primary)
    {
        return new Ordering<T>(query => query.OrderBy(primary));
    }

    public Ordering<T> ThenBy<TKey>(Expression<Func<T, TKey>> secondary)
    {
        return new Ordering<T>(query => transform(query).ThenBy(secondary));
    }

    // And more for the descending methods...

    internal IOrderedQueryable<T> Apply(IQueryable<T> query)
    {
        return transform(query);
    }
}

然后客户端可以创建一个Ordering&lt;T&gt; 传递到存储库,并且存储库可以使用IQueryable&lt;T&gt; 调用Apply。这有意义吗?

示例(有点傻)使用:

var ordering = Ordering<FileInfo>.Create(fi => fi.Length)
                                 .ThenBy(fi => fi.Name);

【讨论】:

  • 是的,这正是我想要的。我怎样才能使用它而不必提供从 repository.GetOrderedBy(Ordering&lt;EntityType&gt;.Create&lt;EntityType, int&gt;(e =&gt; e.Column)) 到使用类型推断的类型:repository.GetOrderedBy(Ordering.Create(e =&gt; e.Column)) 或者这是不可能的,因为 Ordering.Create 是静态的?
  • @TheCloudlessSky:哎呀,这是一个错字。 Create 方法应该只声明一个类型参数。
  • 谢谢乔恩,现在说得通了。你觉得这样做是不是有点矫枉过正?您认为最好返回 IQueryable 并在存储库外进行排序吗?
  • @TheCloudlessSky:好吧,我知道我最近读了一篇文章,说从数据层返回IQueryable&lt;T&gt; 是多么邪恶。我当然认为双方各有利弊...如果您确实想避免这样做,这是表示排序的合理方式。它也可以很好地重复使用(即你不需要每次都创建一个新的):)
【解决方案2】:

这是有争议的,但我认为您的存储库应该只返回数据。让您的消费类担心集合的顺序。

这背后的思考过程非常简单,消费类无论如何决定顺序。因此,他们可以将其传递给您的存储库中手工制作的 orderby 生成器并让它完成工作,或者他们可以只使用 linq 并订购他们从存储库中返回的集合。我认为后一种选择更容易实现,更不容易出现错误,并且在阅读代码时会更有意义,但我再次认为我的观点值得商榷。

【讨论】:

  • 在没有争论的情况下,我大部分时间都同意你的观点,但有时允许存储库处理排序很有用。例如,当实际数据存储可以比您的代码更有效地排序时,或者如果您需要卸载处理以保持当前系统响应。
  • 我同意你的观点,它会更有意义。但是不应该所有 SQL 的执行(又名 .ToList() )都发生在存储库中,而不是更远的地方吗?
  • @TheCloudlessSky:为什么?这确实是 IQueryable 的重点 - 您允许将数据执行推迟到真正需要它之前,这意味着您可以防止对您的数据库执行不必要的复杂查询。让你的存储库做 ToList() 只是意味着你给你的数据库增加了更多的负担......
  • @Reed 或反之亦然。由于您的查询可以在存储库之外进行修改,因此可以在存储库之外构建具有相同效果的查询。如果您将访问权限保留在存储库中,则可以消除该问题。不过,我确实看到这两种解决方案都有很多用途。
【解决方案3】:

我认为直接返回 IQueryable&lt;T&gt; 通常是最好的选择。

例如,假设您有一个方法可以查询您的数据库以返回特定的表或视图。如果您的存储库的客户端只需要前 10 条记录,并使用自定义标准和您不期望的排序,您可以通过返回 IQueryable&lt;T&gt; 来允许这样做。客户端只需添加.Where(...).OrderBy(...).Take(10);

使用IQueryable&lt;T&gt;,跨数据层的调用将自动适应,并且仅拉出 10 条记录。如果您改为返回 List,您的数据库查询将提取每条记录,然后需要在您的应用程序中进行过滤。

这毫无道理地增加了巨大的处理/网络/等开销。

【讨论】:

  • 这是我的困境。根据我的阅读,最好不要为存储库返回IQueryable&lt;T&gt;
  • @TheCloudlessSky:我不知道你是从哪里读到的,但我认为这是个糟糕的建议……除非有特定的理由来阻止这种情况。
  • 首先想到的是,您正在使用此技术将实际数据访问移出存储库并移至任意位置。但是,它确实解决了列出的问题。
  • 请参阅:stackoverflow.com/questions/718624/…,因为我不返回 IQueryable。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-12-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-12-06
相关资源
最近更新 更多