在C#中从MS SQL Server查询数据的最佳实践?


9

在C#中从MS SQL Server查询数据的最佳方法是什么?

我知道在代码中进行SQL查询不是一种好习惯。

是创建存储过程并从C#中使用参数调用它的最佳方法吗?

using (var conn = new SqlConnection(connStr))
using (var command = new SqlCommand("StoredProc", conn) { CommandType = CommandType.StoredProcedure }) {
   conn.Open();
   command.ExecuteNonQuery();
   conn.Close();
}

8
“我知道在代码中进行SQL查询不是一种好习惯。” -imo,那就废话了。
GrandmasterB 2012年

4
如果是在using块中创建的,则不必调用conn.Close()。using块的重点是模仿C ++风格的析构函数清理。从更加文明的时代开始进行优雅的打扫。不像try-catch样式那样随意或笨拙。
Tydus勋爵,12年

2
仅供参考。存储过程也是“代码”。存储过程的重点是允许将过程代码与基于集合的样式SQL混合在一起。因此,无论如何,您都将在代码中查询。对于极高容量的应用程序,有时最好在数据库外执行逻辑以通过添加服务器来实现水平扩展。如果您可以维护单个数据库,但可以使用逻辑多服务器,那么扩展将变得更加容易。
Tydus勋爵,2012年

@GrandmasterB,我们在C#中有大量的SQL内联函数(现在我们的C#LOC接近200万)-六年后,它又重新吸引了我们,因为我们现在需要查找该内联SQL(我们最近雇用了一名SQL专家-因此我们正在进行性能调整)。相信我:您永远不会知道您的应用将变得越来越大,以及将来的情况会如何变化。将不同的语言保存在不同的文件中-即使您只是将其委派以显示资源。您也// SQLCODE可以-但您需要记住要这样做。
乔纳森·迪金森

1
@JonathanDickinson,如果需要,请使用存储的proc。我已经说过很多次了,当您有针对同一数据库运行的不同代码库时,它们很有用。但是,仅仅因为他们是在有用的一些情况犯规自动使使用这些“坏习惯” 所有的时间。如果直接使用SQL语句不会引起问题,那么对于该应用程序来说,这不是一个“坏习惯”。
GrandmasterB

Answers:


10

使用存储过程是一种方法,并且已经广泛使用了很多年。

从C#(或任何.NET语言)与SQL Server数据库进行交互的更现代的方法是使用实​​体框架。实体框架的优点是它提供了更高级别的抽象。

引用Microsoft(https://msdn.microsoft.com/en-us/data/jj590134):

ADO.NET实体框架使开发人员可以通过对概念性应用程序模型进行编程,而不是直接对关系存储架构进行编程来创建数据访问应用程序。目标是减少面向数据的应用程序所需的代码量和维护量。实体框架应用程序具有以下优点:

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

ORM与存储过程的使用涉及权衡,尤其是在安全性和逻辑所在方面。

使用SQL Server进行开发的“经典”方法是使应用程序逻辑驻留在存储过程和程序中,而这些程序和程序仅具有执行存储过程的安全权限,而不能直接更新表。这里的概念是存储过程是应用程序的业务逻辑层。尽管该理论是合理的,但由于各种原因,它已趋于失宠,被以C#或VB等编程语言实现业务逻辑所取代。好的应用程序仍采用分层方法来实现,包括关注点分离等,但是更可能遵循MVC之类的模式。

在ORM中而不是数据库中实现逻辑的一个缺点是负责数据库(DA或DBA)的人员易于调试和测试数据完整性规则。以将钱从支票转入储蓄帐户的经典示例为例,将其作为原子的工作单元来完成,换句话说,将其夹在交易中非常重要。如果仅允许通过存储过程进行这种转移,则DA和审核员对存储过程进行质量检查相对容易。

另一方面,如果这是通过诸如Entity Framework之类的ORM完成的,并且在生产中发现,在极少数情况下,从检查中获取资金却没有节省下来,调试可能会更加复杂,尤其是在可能涉及多个程序的情况下。这很可能是一个边缘情况,可能涉及需要按特定顺序等发生的特殊硬件问题。如何对此进行测试?


或其他ORM。它们很多,与EF相比有各种优点和缺点。
svick 2012年

8
列出ORM的缺点将使此答案更有用。
2012年

6

实际上,基本断言是值得商bat的-在代码中包含SQL或在数据库中包含代码(这是存储过程所在的地方)之间可以进行权衡。

其结果是没有单一的“最佳”,这是无法概括的,因为无论您采用哪种方式都在做出妥协(既获得利益又引入了约束)。

如果您的应用程序和数据库之间存在一对一的对应关系,那么这实际上并不重要。另一方面,如果您拥有由大量应用程序共享的大型核心数据库,则在这些应用程序之间增强数据库内部的一致性就变得更加重要。

更重要的是,您需要担心应用程序的体系结构和层次结构-给适当的数据访问层,无论是使用诸如Entity Framework或NHibernate之类的ORM还是进行更直接的操作,都应将大多数应用程序与该决定隔离开来,无论您是否构建查询或使用存储过程。


我会自由地倾向于与较小的团队(1-3个开发人员)一起从事相对较小的项目-对我来说,使用存储过程比它的价值更大的麻烦,因为考虑到我们的应用程序(和我的技能)的性质,部署新通常,代码比更新模式要容易得多(甚至允许我拥有使更新模式相对简单的代码),而且我能够通过使用通用数据访问代码来强制执行业务规则。这显然是“您的里程可能有所不同”的经典示例。


2
+1表示“没有最好的选择”,-1表示部署代码比部署存储的proc更改要容易,+1表示选择对您的应用有意义并确保DAL隔离的方法-+1。
乔尔·布朗

我确实用“对我来说”来限定部署位,因为对于我而言,情况一直是这样(尤其是对于Web应用程序而言),因为这可能需要对该领域进行一些重写。
Murph 2012年

+1,最有效的选择取决于应用程序和情况。
GrandmasterB 2012年

1
我猜这取决于您的工作条件。如果您没有DBA将您停在生产环境之外,那么将替换存储的proc放入数据库中可能会非常容易。因此,如果没有生产控件将新标记,脚本甚至新编译的代码放到集中式应用程序中,也将变得非常容易。
乔尔·布朗

@JoelBrown-确实-我想,不,我确定我在那里做了很多假设。首先,我的模式是受版本控制的(以粗略但有效的方式),我不是dba,但我很聪明,知道一个人的模式需要保持一致。我主要在Web应用程序上工作,除访问服务器外,您无法进入数据库,而我(只是)将应用程序的部署减少为(或多或少)一个按钮单击...将模式更新集成到部署已列在清单中,但是我总是发现架构更新比代码更新更具压力(易于回滚?)
Murph 2012年

4

只要您对输入进行了参数化,任何方法都是有效的。在代码参数中没有查询的大部分原因是在过去的糟糕时期,许多库迫使您将语句字符串附加在一起,而这正是sql注入攻击的来源。


1
参数化足够了吗?您也不需要消毒吗?
StuperUser 2012年

这取决于您的来源和目的。我从字面上看是他的问题,他是只读的,带有一些参数。在通常情况下,如果参数正确地设置为参数类型异常,或者输入结果不正确,则对脏输入的最坏处理。插入是不同的,通常需要验证,除非您将源视为权威。
比尔

0

最佳实践在这里实在是太夸张了-有很多好的方法可以做到这一点,而您真正选择的方法应该取决于您的应用是什么以及需要做什么。也就是说,您实际上只能做两件事:

  • 正如@Bill指出的那样,您应该始终将查询参数化。字符串构建是SQL注入以及各种难以跟踪的bug的简单方法。更聪明的人想出了如何对SQL进行漏洞处理和转义的方法,因此您无需自己弄清楚。

  • 关闭您的连接。最好的方法是使用using语句包装所有内容,但是try / catch /最后也很酷,如果这会使您浮出水面。但是,请务必确保您使用的连接就像廉价的汽车一样-努力驾驶并快速摆脱它。

我会极力主张的另一种做法是,您应确保将数据访问代码集中在尽可能少的地方。我们不允许前端Web应用程序持有对System.Data.SqlClient的直接引用以帮助强制执行此警告。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.