use*_*964 12 c# database sql-server database-design stored-procedures
我们有一个用C#编写的应用程序,它连接到ms sql服务器.我们使用为每个数据库调用创建一个存储过程,但后来我们注意到使用存储过程给我们带来了很大的缺点,如果我们更改数据库,我们不知道需要更新哪些存储过程.
现在我想知道使用存储过程是坏事还是好事?
jam*_*kes 34
多年来,存储过程已经失宠了.目前用于访问关系数据库的首选方法是通过诸如NHibernate或Entity Framework的O/R映射器.
存储过程需要多更多的工作来开发和维护.对于每个表,您必须编写单个存储过程来创建,检索,更新和删除行,以及为您要进行的每个不同查询添加单独的存储过程.最重要的是,您必须在代码中编写类和/或方法来调用每个存储过程.将其与O/R映射器进行比较:您需要编写的只是类定义,数据库表和映射文件.事实上,现代ORM使用基于约定的方法,无需单独的映射定义.
存储过程会促进不良的开发实践,特别是它们要求您违反DRY(不要重复自己),因为您必须至少在数据库表中键入六次或更多次的字段列表.如果您需要向数据库表添加单个列,这将是一个巨大的痛苦.无法将对象作为参数传递给存储过程,只能传递简单类型(字符串,整数,日期/时间等),这使得几乎不可能避免使用大型参数列表(十几个或更多).
存储过程会促进错误的配置管理实践.这源于DBA应该能够独立于代码本身修改它们的论点.这样做会导致生成的代码版本从未经过集成测试,与源代码管理中的单个特定修订版本不对应,实际上甚至根本不对应源代码管理中的任何修订版本.基本上,如果您没有可审计的记录,端到端,您的代码的哪个版本正在生产中,那么您将遇到麻烦.
存储过程必须与代码主体分开部署.除非您有一个完全自动化的流程来更新它们,否则它们会在一个或多个环境中与主代码库不同步,从而引发错误的风险显着增加.如果您需要使用源代码控制的bisect工具来跟踪引入错误的修订版,则这尤其成问题.
存储过程不灵活.如果您想以几种不同的方式查询数据(不同的排序顺序,懒惰与急切加载,分页等),您需要为所有不同的用例编写大量单独的存储过程,而ORM为您提供灵活的,强大的查询语言(例如Linq to NHibernate).
存储过程要求您重新发明轮子.如果您需要乐观并发,或工作单元模式,延迟加载,或身份映射,或父/子集合的处理,或缓存,或类层次结构映射,或几乎您阅读的任何其他设计模式在Martin Fowler的书"企业应用程序架构模式"中,您需要自己重建这个功能,而O/R映射器可以直接提供所有这些,甚至更多.通常,您最终会使用复制和粘贴代码重新发明这些轮子,这也是一种不好的做法.
存储过程很难进行单元测试.使用ORM,您可以模拟数据库代码,以便能够快速测试业务逻辑.使用存储过程,您必须从头开始重建整个测试数据库.
存储过程没有任何性能优势.通过线路传递sproc的名称而不是SQL字符串所获得的(微小)增益很容易被这样一个事实所抵消,即你很可能最终使用相同的程序调用相同的程序两到三次同一个请求中的参数,而ORM会在其身份图中查看并说:"嘿,我已经检索过那个,根本不需要再往返一次." 此外,声称存储过程缓存在服务器上,而ad-hoc SQL不是,这是Frans Bouma在他的博客文章" 存储过程很糟糕,很难? " 中被破坏的神话.
存储过程在安全性方面提供很少或没有优势,并且不保护您免受SQL注入漏洞的影响.例证:
create procedure GetUsers(@SortOrder nvarchar(50))
as
begin
declare @sql nvarchar(100)
set @sql = 'SELECT * FROM Users ORDER BY ' + @SortOrder
exec @sql
end
Run Code Online (Sandbox Code Playgroud)
当然,您可以编写没有SQL注入漏洞的存储过程,但是您可以通过使用参数化查询在业务层中编写没有SQL注入漏洞的临时SQL.将SQL注入保护归因于存储过程,而不是将SQL字符串粉碎在一起,这是一个红色的鲱鱼,完全是误导性的.
我相信SP适用于DB中的计算/数据操作/报告数据源.
当它仅用于表格行的数据检索/更新时,您将遇到整个受伤的世界.
这是一些数据访问层遵循的方法,单个行的数据检索sps可能会变得很痛苦.
所以不,我不会推荐这是最好的方式.
这不是SP问题,这是您的开发过程中的一个问题.如果您没有所需的信息 - 那就搞定吧.
您可以创建一个简单的可视化地图,显示您的表架构和相关的SP.如果您的数据库太大而无法进行可视化映射,请添加由SP组成的公共文本文件及其所依赖的表的名称.
无论如何,数据库越大,将模式的详细信息内联到应用程序代码中的工作就越糟糕.当您使用SP时,您可以保证此功能不会加倍,并且大多数更改将在数据库端发生,而无需重新编译和重新分发应用程序.
UPD
我忘了提一件事.良好的数据库工具提供了查找每个SP的相关表的简便方法.例如,Microsoft SQL Management Studio中的SP上下文菜单中有"查看依赖项"项.
我从事过很多使用存储过程的项目。基本上,业务层移到了数据库,因为团队领导对他之前工作中遇到的某个oracle大师印象深刻。
存储过程代码比 C#(在 Visual Studio 中)更难维护,因为工具更差,调试更难等。
同时,为您的数据规则提供清晰的接口。考虑将在数据库上执行哪些查询可能是一件好事。
尽量将数据库生成和迁移(更新)代码保留在源代码管理中。如果您真的需要它们,请在其中包含存储过程。保持存储过程逻辑尽可能简单(不要做任何业务逻辑,只做一致性风格的东西)。甚至可能从更抽象的表示中生成它们(以及调用它们的 C# 代码)。