【问题标题】:Correct/Ideal way to manage SQL Connections and Transactions - C#管理 SQL 连接和事务的正确/理想方式 - C#
【发布时间】:2013-05-20 13:18:48
【问题描述】:

我有一个 C# 应用程序,它跨多个函数和类进行许多 SQL 调用。数据库是 SQL Server 2008 R2。目前每个类或函数在需要读/写数据到数据库时都会打开自己的 SQL 连接:

using (SqlConnection connection = new SqlConnection(connectionString))

这是处理连接管理的正确方法吗?另外我刚刚开始添加事务,所以如果我没记错的话,事务应该在同一个连接上完成。这是一些代码的示例:

try
{
   Class1 class1 = new Class1();
   class1.data = "somedata";
   class1.save(); // new sql connnection

   Class2 class2 = new Class2();
   class2.data = GetSomeData(); // new sql connection
   class2.save(); // new sql connection
}

catch (Exception e)
{
}

我查看了 TransactionScope 和 SqlTransaction,但我不确定我当前的连接结构是否适合此实现。任何指导将不胜感激。

【问题讨论】:

  • 2 类数据是否依赖于 1 类结果?

标签: c# sql-server ado.net transactions


【解决方案1】:

为每个操作“打开”一个新连接并不像人们想象的那样耗费资源。

原因是今天所有的 ADO.NET 提供程序都在内部使用连接池。这意味着每次您.Close() 连接时,它实际上只是返回到池中。

当您开始在事务中涉及多个连接时,性能就会受到影响,因为这意味着事务被提升为分布式事务。也就是说,涉及第三方事务管理器并控制事务。

所以答案是,只要您的实体没有依赖关系,就没有理由从事务开始或停止使用多个连接。

什么时候需要交易?

但是,如果实体 A 依赖于实体 B,则必须使用事务。

依赖关系也可以由业务规则指定。让我们举个例子。

您有一笔银行转账,应该从账户 A 提取资金并存入账户 B。如果没有交易,资金可能会从账户 A 提取但不会存入 B(由于错误)。

【讨论】:

  • 感谢您的回复。从某种意义上说,实体是依赖的,如果一个失败,一切都需要回滚。在多个连接的情况下,我是否可以创建一个事务,将其传递给每个类或函数,然后在完成提交整个事务?
  • 如果有多个连接,您将不得不通过TransactionScope使用分布式事务
  • 啊好吧,这很有道理。我猜测在我的场景中更好的方法可能是将所有所有内容都包装在一个创建新 sql 连接的 using 中,然后将该连接传递给每个类的构造函数。
【解决方案2】:

您可以将两个保存调用包含在启动事务的 using 子句中。

每当您的系统想要启动连接时,它都会检查父事务(如果有的话)。

http://msdn.microsoft.com/en-us/library/system.transactions.transactionscope.aspx查看样品

【讨论】:

    【解决方案3】:

    至少创建一个处理对象持久性的数据访问类——只需将对象传递给知道如何处理它的类方法即可。只有当类是一个孤立的实体时,在整个类中传播持久性代码才有意义,而这很可能不是(大多数对象与其他对象交互)。您应该使用 .NET--SQLMetal(命令行)或 Visual Studio 中的 LINQ-to-SQL ORM 设计器中提供的 ORM 功能。这样您就不会遇到现在遇到的问题,而是可以专注于您真正想要对代码执行的操作。

    【讨论】:

      【解决方案4】:

      是的,在同一代码块中包含 using 子句来执行对象和 SQL Server 之间的实际数据封送处理是正确的方法。原因很简单,如果发生错误,那么您不会泄漏连接,这可能会导致其他非常难以定位的问题。

      问题实际上是您是否应该对每个单独的类进行save 调用,或者是否应该使用数据访问提供程序之类的东西来处理与数据库服务器的通信。

      如果实体 B 依赖于实体 A,这意味着必须保存 A 并返回 ID,然后 B 才能正确保存,那么您可以选择。要么 A 需要引用 B 并处理它的保存,要么将两个对象都传递给处理正确保存的数据访问类。此处的一个示例是,如果实体 A 是订单标题,而 B 是行项目。在这种情况下,A 应该引用 B 的集合。当调用 A.Save() 时,它应该迭代该集合并调用 B.Save() 本身。

      无论哪种方式都可以相当容易地在单个连接的上下文中实现事务。

      【讨论】:

      • 感谢您的回复。在上面的示例中,实体不相互依赖,但如果其中一个失败,一切都需要回滚。在这种情况下,我是否必须保留一个事务列表,以便在失败时回滚每个事务?
      猜你喜欢
      • 2021-06-18
      • 1970-01-01
      • 2011-11-01
      • 2015-04-04
      • 1970-01-01
      • 1970-01-01
      • 2016-10-14
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多