【问题标题】:Is it a good approach to query the database only through stored procedures?仅通过存储过程查询数据库是一种好方法吗?
【发布时间】:2013-11-25 16:53:33
【问题描述】:

当我开发 ASP.NET 网站时,我真的很喜欢将实体框架与数据库优先或代码优先模型(+ asp.net mvc 控制器脚手架)一起使用。

对于需要访问现有数据库的应用程序,我自然想到创建一个数据库模型并使用 asp.net mvc 脚手架在几分钟内完成所有基本的 CRUD 操作,几乎没有开发成本。

但我与一位朋友讨论过,他告诉我仅通过存储过程访问存储在数据库中的数据是最好的方法

因此,我的问题是,您如何看待这句话?为数据库中的表上的任何所需操作创建存储过程是否更好(例如,在此表上创建和读取,仅在另一个表上更新和删除,...)?这样做而不是使用从数据库中的表创建的数据库优先模型有哪些优点/缺点?

我起初的想法是,通过存储过程完成所有事情会使开发成本翻倍,因为您必须编写这些存储过程,而 Entity Framework 只需单击几下就可以提供DbContext,从而允许我使用 LINQ over Entities , ... 但是后来我阅读了一些关于所有权链的内容,这些内容可能会通过仅设置执行存储过程的权限而不设置对表的任何操作(选择、插入、更新、删除)的权限来提高安全性。

感谢您的回答。

【问题讨论】:

  • 除了提到的优点之外,我不禁想起了一个缺点: - 将大字符串作为 varchars(参数)传递是非常不合适的,你必须将它们分成多个部分等。所以如果你想通过加入ids 作为 varchar 例如 ("4,523,5,78,...") 它的长度非常有限 (nvarchar(max))...更容易在您的业务逻辑中创建一个 sql 语句。
  • @sjkm: nvarchar(max) 可以容纳十亿个字符 - 在我看来并不完全非常有限 ....
  • 是的,甚至 10.37 亿,但我说的是巨大的字符串。一旦在报告系统中出现这种情况,我必须处理它。

标签: c# sql-server entity-framework stored-procedures linq-to-sql


【解决方案1】:

这是一个成本效益分析。作为一个专注于数据库的人,我同意这种说法。这是最好的。它还使您的代码更易于阅读(没有疯狂的 sql 语句丑化它)。通过缓存的执行计划提高性能。无需重新编译代码即可轻松修改查询等

与我一起工作的许多人对编写 SPROC 并不十分熟悉,因此与他们一起使用 SPROC 往往是一场持久战。就我个人而言,我认为没有任何理由将 SQLStatments 隐藏在您的代码中。他们倾向于回避他们,因为这对他们来说是更多的工作。

【讨论】:

  • 这就是为什么我更喜欢使用实体框架,因此只“玩”实体。所以我确实不需要编写任何 SQL 和/或 T-SQL。因此使用诸如实体框架之类的 ORM 是一种不好的做法吗?
  • @ggobbe 大声笑这是一个加载的问题。我不使用 ORMs b/c 我喜欢控制我的 SQL 和模式等。使用 ORM 并不是坏习惯,它使开发人员的生活更轻松。但是您并不总是知道传递给数据库的 SQL 语句,因此不知道它们将如何执行。
  • ORM 适用于懒惰或没有准备的开发人员。它对于小型上下文很有用,您可以在其中维持性能命中。使用SP是要走的路。期间。
  • @LittleSweetSeas 好吧,我试图对 ORM 保持开放的态度......但在我内心深处我同意你的观点:)
【解决方案2】:

是的,这是一个很好的方法。

这是否是最佳方法,这取决于很多因素,其中一些你甚至还不知道。

一个重要的因素是会有多少进一步的开发,以及多少维护。如果最初的开发是整个工作的重要组成部分,那么您应该使用一种尽可能快速和简单的方法。

如果您将长期使用和维护系统,则应少关注初始开发时间,而应多关注系统启动并运行后对其进行更改的难易程度。使用存储过程是减少代码对确切数据布局的依赖的一种方法,并且允许您在没有大量停机时间的情况下进行更改。

请注意,它不一定是存储过程和实体框架之间的选择。您还可以将存储过程与 Entity Framework 一起使用。

【讨论】:

    【解决方案3】:

    这主要是一个基于意见的问题,答案可能取决于具体情况。使用存储过程绝对是查询数据库的最佳方法之一,但自从实体框架出现以来,它被广泛使用。 Entity Framework 的优势在于它提供了更高层次的抽象。

    实体框架应用程序提供以下好处:

    • 应用程序可以根据更加以应用程序为中心的概念模型工作,包括具有继承的类型、复杂的成员、
      和关系。
    • 应用程序摆脱了对特定数据引擎或存储架构的硬编码依赖。
    • 概念模型和存储特定架构之间的映射可以在不更改应用程序代码的情况下更改。
    • 开发人员可以使用一致的应用程序对象模型,该模型可以映射到各种存储模式,可能在
      中实现 不同的数据库管理系统。
    • 可以将多个概念模型映射到一个存储架构。
    • 语言集成查询 (LINQ) 支持为针对概念模型的查询提供编译时语法验证。

    您也可以查看这个相关问题Best practice to query data from MS SQL Server in C Sharp?

    【讨论】:

      【解决方案4】:

      以下是一些存储过程的优点

      • 使用存储过程将多个语句封装为单个事务
      • 使用临时表实现业务逻辑
      • 通过用于捕获/记录错误的表来更好地处理错误
      • 参数验证/域验证可以在数据库级别完成
      • 通过强制选择索引来控制查询计划
      • 使用 sp_getapplock 可随时强制执行单个过程

      此外,实体框架将为您发出的每个请求增加开销,因为实体框架将为每个查询使用反射。因此,通过实现存储过程,您将获得时间,因为它被编译而不是像普通实体框架查询那样每次都被解释。

      下面的链接给出了你应该使用实体框架的一些理由

      http://kamelbrahim.blogspot.com/2013/10/why-you-should-use-entity-framework.html

      希望能给你一点启发

      【讨论】:

        【解决方案5】:

        所以我会给你一个建议,这将是我做过的事情,但没有多少人会说“我这样做”。

        所以,是的,我在使用 ADO.NET 时使用了存储过程。

        我也(有时)使用 ORM,比如 NHibernate 和 EntityFramework。

        当我使用 ADO.NET 时,我使用存储过程。

        当你从数据库中获取数据时,你必须把它变成 DotNet 端的东西。

        最快的方法是将数据放入 DataTable 或 DataSet。

        我不再赞成这种方法。虽然它可能有助于快速开发(“只是将数据填充到数据表中”)......但它不适用于维护,即使维护只有 2-3 个月。

        那我把数据放进去做什么呢?

        我创建 DTO/POCO 对象并将数据库中的数据混合到这些对象中。

        例如。

        NorthWind 数据库有

        Customer(s)
        Order(s)
        and OrderDetail(s)
        

        所以我创建了一个名为 Order.cs、Order.cs 和 OrderDetail.cs 的 csharp 类。 这些仅包含实体的属性。大多数时候,属性简单地反映了该实体在数据库中的列。 (Order.cs 具有模拟 Select * from dbo.Order 的属性,例如 OrderID = 123)。

        然后我创建一个子集合对象

        public class OrderCollection : List<Order>{}
        

        然后父对象得到一个属性。

        public class Customer ()
        {
        /* a bunch of scalar properties */
        public OrderCollection Orders {get;set;}
        }
        

        所以现在你有了一个存储过程。它会获取数据。

        当该数据返回时,获取它的一种方法是使用 IDataReader。 (.ExecuteReader)。

        当这个 IDataReader 返回时,我循环遍历它,并填充 Customer(.cs)、Orders 和 OrderDetails。

        这是基本的,穷人的 ORM(对象关系映射)。

        回到我如何编写存储过程,我将编写一个返回 3 个结果集(一个 db 命中)并返回有关客户、订单(如果有)和 OrderDetails 的信息的过程(如果有的话)。

        请注意,我不会做很多 JOINING。
        当您执行“Select * from dbo.Customer c join dbo.Orders o on c.CustomerID = o.CustomerId”时,您会注意到您在第一列中获得了冗余数据。这是我不喜欢的。

        我更喜欢多个结果集而不是加入并带回具有冗余数据的单个结果集。

        现在是一个特殊的小技巧。

        每当我从一个表中选择时,我总是选择该表上的所有列。

        因此,每当我编写需要客户数据的存储过程时,我都会执行 从 dbo.Customer 中选择 A,B,C,D,E,F,G 其中 (......)

        现在,很多人会这么说。 “为什么你带回的信息比你需要的多?”

        好吧,无论如何,真正的 ORM 都会这样做。所以我是反映这一点的穷人。 而且,我用于从存储过程中获取结果集以将其转换为对象实例的代码......保持一致。

        因为如果您编写 3 个存储过程,并且每个都从客户表中选择数据,但是您选择不同的列和/或以不同的顺序,那么您的“对象映射器”代码需要为每个存储过程提供一个方法。

        ADO.NET 的这种方法对我很有帮助。 而且,一旦我的团队将 ADO.NET 换成真正的 ORM,由于我们从一开始就使用 ADO.NET 的方式,这种转换非常轻松。

        快速的经验法则:

        1.  If using ADO.NET, use stored procedures.
        2.  Get multiple result-sets, instead of redundant data via joins.
        3.  Make your columns consistent from any table you select from.
        4.  Take the results of your stored procedure call, and write a "hydrater" to take that info and put into your domain-model as soon as you can.  (the .cs classes)
        

        多年来,这对我很有帮助。

        祝你好运。

        【讨论】:

          【解决方案6】:

          在我看来:

          1. 存储过程是用 PL/SQL 或 T-SQL 等大型数据库“语言”编写的
          2. 通常无法在您编写 UI 的同一 IDE 中调试存储过程。
          3. 当出现问题时,存储过程不会提供太多反馈。
          4. 存储过程不能传递对象。
          5. 存储过程隐藏了业务逻辑。

          来源: http://www.codinghorror.com/blog/2004/10/who-needs-stored-procedures-anyways.html

          【讨论】:

          • 我不得不在大多数情况下不同意你的观点。 OP 正在使用 SQLServer,它可以在 VS 中进行调试。提供反馈很容易。为什么还要将对象传递给数据库?而且业务逻辑在存储过程中比在准编译代码中更明显。
          猜你喜欢
          • 2020-09-10
          • 2011-06-23
          • 2019-02-22
          • 1970-01-01
          • 2019-09-14
          • 2017-08-01
          • 2018-01-02
          • 2010-12-02
          相关资源
          最近更新 更多