如何减少代码中的SQL查询量?

我正在编写一个与MS-SQL Server数据库通信的大型C#应用程序。

随着应用程序的扩大,我发现自己正在编写越来越多的“样板”代码,其中包含各种类和形式的各种SQL查询,如下所示:

using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Windows.Forms;

public class SomeForm : Form
{
    public void LoadData(int ticketId)
    {
        // some multi-table SQL select and join query
        string sqlQuery = @"
            SELECT TOP(1) [Foo].Id AS FooId,[Foo].Name AS FooName,[Foo].Address AS FooAddress
                        [Bar].Name AS BarName,[Bar].UnitPrice AS BarPrice,[Bif].Plop
            FROM        [dbo].[Foo]
            INNER JOIN  [dbo].[Bar]
            ON          [Bar].Id = [Foo].BarId
            INNER JOIN  [dbo].[Bif]
            ON          [Bar].BifId = [Bif].Id
            WHERE       [Foo].TicketId = @ticketId";

        SqlCommand sqlCmd = new SqlCommand();
        sqlCmd.CommandText = sqlQuery;
        sqlCmd.Parameters.AddWithValue("@ticketId",ticketId);

        // connection string params etc and connection open/close handled by this call below
        DataTable resultsDataTable = SqlQueryHelper.ExecuteSqlReadCommand(sqlCmd);

        if (resultsDataTable.Rows.Count > 0)
        {
            var row = resultsDataTable.Rows[0];

            // read-out the fields
            int fooId = 0;
            if (!row.Isnull("FooId"))
                fooId = row.Field<int>("FooId");

            string fooName = "";
            if (!row.Isnull("FooName"))
                fooName = row.Field<string>("FooName");

            // read out further fields...

            // display in form
            this.fooNameTextBox.Text = fooName;
            // etc.
        }
    }
}

此项目中有数十种形式在概念上都做相同的事情,只是使用不同的SQL查询(选择了不同的列,等等)。 并且每次打开表单时,都会不断查询数据库。

对于本地数据库服务器而言,速度还可以,但是通过慢速VPN使用该应用程序会很痛苦。

是否有更好的方法来减少查询数据库的数量?某种形式的将数据库缓存在内存中,然后对内存中的数据执行查询?

我已经在项目中的数据源中添加了一些数据表,但无法理解如何执行上述复杂查询。

有更好的方法吗?

感谢您的所有建议!

dsfvdkjdfg4ah94njknh 回答:如何减少代码中的SQL查询量?

我认为没有建议像使用框架来访问数据集那样解决您的问题。问题不在于编写SQL或LINQ查询。在某些时候,您总是必须向数据库或数据集调度查询。

问题出在查询数据库的 where
您关于编写“包含各种类和形式的各种SQL查询的代码的声明” 使我感到不寒而栗。我最近在一家公司工作,他们做的完全一样。结果,他们无法再维护其数据库。维护仍然是可能的,但是仅是基本的,非常昂贵/费时且非常令人沮丧的-因此没有人喜欢这样做,因此没有人这样做,结果它变得越来越糟。查询变得越来越慢,唯一可能的快速解决方案是为数据库服务器购买更多带宽。

实际查询分散在整个项目中,因此无法改进/重构查询(并识别查询)或改进表设计。但最糟糕的是,他们无法切换到更快或更便宜的数据库模型,例如图形结构的数据库,尽管迫切需要并且迫切需要这样做。它使您在客户面前显得马虎。在这样的环境下工作绝对没有乐趣(所以我离开了)。听起来不好吗?

您确实应该将数据库和SQL与业务代码分离。所有这些都应该隐藏在界面的后面:

IRepository repository = Factory.GetRepository();

// IRepository exposes query methods,which hide 
// the actual query and the database related code details from the repository client or business logic
var customers = repository.GetCustomers();

在您的项目中传播此代码不会造成任何伤害。它将提高可维护性和可读性。由于您要隐藏/封装实际数据库,查询和查询语言等所有详细信息,因此它将数据持久性与实际业务逻辑分开。如果要切换到另一个数据库,只需实施一个新的IRepository即可修改现有查询。更改数据库不会破坏应用程序。

所有查询都在一个位置/一层中实现。在谈论查询时,它也包括LINQ查询(这就是为什么使用诸如Entity Framework这样的框架不能解决您的问题的原因)。您可以使用依赖项注入或Factory模式来分发IRepository的实现。这甚至允许在运行时期间在不同数据库之间切换,而无需重新编译应用程序。

使用此模式(存储库模式)还可以使诸如Entitiy Framework之类的框架与业务逻辑脱钩。

看看存储库模式。如果还不算太晚,您应该开始重构现有代码。如果保持原样,价格太高。

关于缓存,我知道数据库或DBMS已经非常非常有效地处理了数据缓存。您可以做的是

  • 在本地缓存数据,例如如果数据不会远程更改(因为 其他一些客户端会对其进行修改),例如本地客户端设置。这个 您可以大大减少网络流量的方式。
  • 在本地缓存数据,例如如果您经常访问此数据集,并且不太可能发生远程更改或将产生影响。您可以定期更新本地数据存储或使其无效,例如在一段时间后强迫客户端调度新查询以更新缓存。
  • 您还可以使用LINQ在本地过滤数据。这也可能减少 网络流量,尽管您最终可能会读取更多数据 必要。通常,通过 DBMS。
  • 您可以考虑升级数据库服务器或VPN以增加 带宽。
  • 建立索引还将大大缩短查找时间。
  • 您应该考虑重构所有SQL查询。有许多 有关如何提高SQL查询性能的文章。你的方式 建立查询将对性能或 执行时间,尤其是大数据。
  • 您可以使用数据虚拟化。当您只能向用户显示20条记录时,从数据库中提取数千条记录是没有意义的。用户滚动视图时提取更多数据。甚至更好的是,显示预选列表,例如并允许用户搜索感兴趣的数据。这样,您可以仅读取 用户明确要求的数据。因为通常用户只对很少的记录感兴趣,所以这将大大提高整体性能。

之前引入接口(依赖反转)

以下示例旨在说明这是体系结构或设计问题,而不是框架或库问题。 库或框架可以在不同的级别上提供帮助,但不能解决问题,这是通过在整个业务代码中分布特定于环境的查询而引入的。查询应始终保持中立。在查看业务代码时,您应该不知道该数据是从文件还是数据库中获取的。此细节必须隐藏或封装。

当您在整个业务代码中散布实际的数据库访问代码(无论是直接使用纯SQL还是借助框架)时,如果没有连接数据库,就无法编写单元测试。这是不希望的。这会使测试变得过于复杂,并且测试将不必要地缓慢进行。您想测试您的业务逻辑而不是数据库。这是单独的测试。您通常希望将数据库模拟掉。

问题:
您需要跨应用程序模型或业务逻辑从多个位置的数据库中获取数据。

最直观的方法是随时随地在需要数据的地方调度数据库查询。这意味着,当数据库是Postgre数据库时,所有代码当然都将使用PostgreSQL或诸如Entity Framework或ORM之类的框架。如果您决定更改数据库或DBMS,例如对于某些Oracle或想使用其他框架来管理您的实体,您将不得不触摸并重写使用PostgreSQL或Entity Framework的每个代码。

在大型企业应用程序中,这就是迫使您的公司继续保持现有状态并使团队梦想着拥有更美好世界的原因。挫败感会上升。维护数据库相关代码几乎是不可能的,容易出错且耗时。由于实际的数据库访问不是集中的,因此重写与数据库相关的代码意味着可以爬网整个应用程序。最糟糕的情况是无人理解的SQL查询字符串的传播,没有人理解或记得。不可能迁移到新数据库或重构查询以提高性能,而又不会浪费宝贵和昂贵的时间和团队资源。

想象一下,以下简化的符号方法在应用程序的业务逻辑中以某种形式重复出现,可能访问不同的实体并使用不同的过滤器,但使用相同的查询语言,框架或库。假设我们找到了类似的代码一千次:

private IEnumerable GetCustomers()
{
  // Use Entity Framework to manage the database directly
  return DbContext.Customers;
}

我们已经将框架紧密地结合到了框架中,因为它已深深地融入我们的业务代码中。该代码“知道”数据库的管理方式。它了解Entity Framework,因为它必须在任何地方使用其类或API。
证明是,如果您想用其他框架替换Entity Framework或只想删除它,则必须在数千个地方重构代码-在应用程序中使用该框架的任何地方。

之后,引入接口(依赖关系反转)并封装所有数据库访问权限

Dependency Inversion将通过引入接口来帮助消除对具体类的依赖。由于我们更喜欢在组件和类之间使用松散耦合来增强灵活性,可测试性和可维护性,因此在使用辅助框架或纯SQL方言时,我们必须包装此特定代码并将其隐藏在接口(存储库模式)后面。

我们现在没有引入一千个地方来显式使用数据库框架或SQL或LINQ查询来读取,写入或过滤数据,而是引入了接口方法,例如GetHighPriorityCustomersGetAllCustomers。如何提供数据或从哪种数据库中获取数据是细节,这些细节只有此接口的实现才知道。

现在,应用程序不再直接使用任何框架或数据库特定的语言:

interface IRepository
{
  IEnumerable<Customer> GetHighPriorityCustomers();
  IEnumerable<Customer> GetAllCustomers();
}

以前的一千个地方现在看起来像:

private IRepository Repository { get; } // Initialized e.g. from constructor
private IEnumerable GetCustomers()
{
  // Use a repository hidden behind an interface.
  // We don't know in this place (business logic) how the interface is implemented
  // and what classes it uses. When the implementation changes from Entity Framework to something else,no changes have to be made here (loose coupling).    
  return this.Repository.GetAllCustomers();
}

IRepository的实现:

class EntityFrameworkRepository : IRepository
{

  IEnumerable<Customer> GetAllCustomers()
  {
    // If we want to drop Entity Framework,we just have to provide a new implementation of IRepository
    return DbContext.Customers;
  }

  ...
}

现在,您决定使用纯SQL。唯一要做的更改是实现一个新的IRepository,而不是更改千位删除专用代码:

class MySqlRepository : IRepository
{
  // The caller still accesses this method via the interface IRepository.GetAllCustomers()
  IEnumerable<Customer> GetAllCustomers()
  {
    return this.Connection.ExecuteQuery("SELECT * FROM ...");
  }

  ...
}

现在,您决定用Microsoft SQL替换MySQL。您要做的就是实现一个新的IRepository
您可以交换任何数据库,并更改查询语言或引入帮助程序框架,而不会影响您的原始业务逻辑。写入后,就再也不必动手了(至少是有关数据库的更改)。
如果将实现转移到单独的程序集中,甚至可以在运行时交换它们。

,

我可以建议几件事:

  1. 您无需使用ADO.NET即可切换到Entity Framework,而可以使用LINQ to SQL / LINQ to EF(较新版本)并使用 C#写下查询语言,不必担心查询 SQL
  2. 使用存储过程,SQL函数,视图-记录在 SQL Server 数据库中,该调用由 SQL Server 缓存提供更有效的执行,安全性和可维护性。
  3. 为了使您的查询对数据库表的效率更高,请考虑对这些表使用Full-Text Index,这些表是您在过滤操作(例如搜索)中更常使用的数据。
  4. 在您的 C#代码中使用Repository and Unit of Work模式(包括与Entity Framework的集成),这些模式实际上可以完成您想要的事情,例如,收集大量的 SQL > 查询并立即将其发送以由 SQL Server 执行,而不是一个接一个地发送 Queries 。这不仅将大大提高性能,而且将使您的编码尽可能地简单。

    注意:查询的问题之一不仅与查询的执行有关,而且还与每次需要执行特定查询时打开和关闭SQL数据库连接有关。通过存储库和工作单元设计模式方法可以解决此问题。

  5. 根据您的业务需求,对数据使用In MemoryDatabase缓存,这会重复出现在许多用户身上。
,

除实体框架外,还有另一种解决方案。

例如,您可以使用The Sharp Factory

这是一种商业产品,但它不仅映射诸如Entity Framework之类的数据库对象,而且还基于层创建完整的存储库。

如果您愿意为此付费,它比EF好。

实体框架有一些缺点。例如,您的存储库将在整个图层中泄漏的事实。

因为您需要在实体的所有使用者上引用Entity Framework,因此,即使您的体系结构看起来正确,在运行时,您仍然可以在不知不觉中从上层执行sql查询。

本文链接:https://www.f2er.com/2437156.html

大家都在问