回复:CRUD 操作。它提取的数据比需要的多是一件坏事吗?

RE: CRUD operations. Is it pulling more data than is needed a bad thing?

RE: CRUD 操作...提取的数据比需要的多是坏事吗?

首先让我说我确实搜索过这个答案。现在断断续续有一段时间了。我确定它之前已经 asked/answered 但我似乎找不到它。大多数文章似乎都针对如何执行基本的 CRUD 操作。我真的很想深入了解最佳实践。话虽如此,这是我为示例目的而模拟的示例模型。

public class Book
{
    public long Id { get; set; }
    public string Name { get; set; }
    public decimal AverageRating { get; set; }
    public decimal ArPoints { get; set; }
    public decimal BookLevel { get; set; }
    public string Isbn { get; set; }
    public DateTime CreatedAt { get; set; }
    public DateTime PublishedAt { get; set; }

    public Author Author { get; set; }
    public IEnumerable<Genre> Genres { get; set; }
}

我正在使用 ServiceStack 的 OrmLite,尽可能将字符串查询迁移到对象模型绑定。这是一个 C# MVC.NET 项目,使用 Controller/Service/Repository 层和 DI。我最大的问题是读取和更新操作。以阅读为例。这里有两个方法(只写了我认为相关的)作为示例。

public class BookRepository
{
    public Book Single(long id)
    {
        return _db.SelectById<Book>(id);
    }

    public IEnumerable<Book> List()
    {
        return _db.Select<Book>();
    }
}

不管现实世界需要如何改变,问题只是返回的信息太多。假设我要向用户显示图书列表。即使 List 方法被编写为不提取嵌套方法(作者和流派),它也会包含未使用的属性的数据。

看来我要么学会忍受获取不需要的数据,要么编写一堆额外的方法来更改提取的属性。使用 Single 方法,这里有几个例子...

public Book SinglePublic(long id): Returns a few properties
public Book SingleSubscribed(long id): Returns most properties
public Book SingleAdmin(long id): Returns all properties

对我来说,必须为大多数表写出这样的方法似乎不太容易维护。但是,几乎总是在大多数呼叫中获得未使用的信息会影响性能,对吗?我必须错过一些东西。任何帮助将不胜感激。随便分享一个 link,给我一个 PluralSight 视频看,推荐一本书,随便什么。我对任何事情都持开放态度。谢谢。

我不能说 ORM Lite,但是对于 Entity Framework,ORM 会向前看,并且只有 return 列是完成后续执行所必需的。如果你将它与视图模型结合起来,你就处于一个非常好的位置。因此,例如,假设您有一个网格来显示您的书名。您只需要数据库中的一部分列即可。您可以像这样创建一个视图模型:

public class BookListViewItem{
  public int Id {get;set;}
  public string Title {get; set;}

  public BookListView(Book book){
    Id = book.Id;
    Title = book.Title;
  }
}

然后,当你需要的时候,像这样填写:

var viewModel = dbcontext.Books
                         .Where(i => i.whateverFilter)
                         .Select(i => new BookListViewItem(i))
                         .ToList();

这应该将生成的 SQL 限制为仅请求 idtitle 列。

在Entity Framework中,这叫做'projection'。参见:

https://social.technet.microsoft.com/wiki/contents/articles/53881.entity-framework-core-3-projections.aspx

作为一般规则,您应该避免 pre-mature 优化,并且始终首先从最简单和最高效的解决方案开始,因为避免 complexity & large code-base sizes 应该是您的首要任务。

如果你只获取一行,你绝对应该从只使用一个 API 开始并获取完整的 Book 实体,我个人也会避免 Repository 抽象,它我认为这是一个额外的不必要的抽象,所以我只是在你的控制器或服务中直接使用 OrmLite APIs,例如:

Book book = db.SingleById<Book>(id);

您肯定不会注意到 RDBMS 网络调用的 I/O 成本中的额外未使用字段,并且您的应用程序和 RDBMS 之间的延迟和带宽比在线上的其他信息大得多通过互联网。为了减少未使用的字段而使用多个 API 会增加不必要的复杂性,增加 code-base 大小/技术债务,降低代码的可重用性、可缓存性和可重构性。

何时考虑对单个实体进行多个数据库调用:

  1. 您已收到反馈并分配了一项任务来提高 page/service
  2. 的性能
  3. 您的实体包含大型斑点文本或图像等二进制字段

第一个是避免 pre-mature 优化,首先关注简单性和生产力,然后再进行优化以解决已知的可实现的性能问题。在这种情况下,首先分析代码,然后如果它显示问题出在数据库查询上,您可以仅针对 returning API/page.

所需的数据进行优化

为了提高性能,我通常会首先评估缓存是否可行,因为它通常是最省力/最大价值的解决方案,您可以轻松缓存 APIs 和 [CacheResponse] attribute which will cache the optimal API output for the specified duration or you can take advantage of caching primitives in HTTP 以避免需要return 任何 non-modified 在线资源。

为了避免在没有大量数据的情况下进行不同查询的第二个问题,我会将其提取到不同的 1:1 行中,并且仅在需要大数据时才检索它行大小会影响访问 table.

的整体性能

汇总数据的自定义结果

所以我很少有不同的 APIs 来访问单个实体的不同字段(更可能是由于额外的连接),但 returning 相同的多个结果entity 我会有一个不同的优化视图,只包含所需的数据。这个现有的答案显示了在 OrmLite 文档中 (See also Dynamic Result Sets 的一些方法。

我通常更喜欢将 custom Typed POCO 与我希望 RDBMS 为 return 的字段一起使用,例如在摘要中 BookResult 实体:

var q = db.From<Book>()
  .Where(x => ...);
var results = db.Select<BookResult>(q);

这都与手头的任务有关,例如returned 的结果越少或访问 Page/API 的并发用户越少,您使用多个优化查询的可能性就越小,而对于 public API 有 1000 个并发用户的经常访问的功能我肯定希望经常进行分析并优化每个查询。虽然这些案例通常会从利益相关者那里得到明确说明,他们将“性能是一个特征”作为主要 objective 并相应地分配时间和资源。