2010-04-02 216 views
9

我设计了我们的框架的数据访问部分,以便每次业务对象(BO)需要与数据库进行交互时,都必须打开连接,调用数据访问层(执行查询),然后关闭连接。然后,如果需要在事务中运行,它将打开连接,开始事务,调用数据访问层(执行查询),然后提交事务,关闭事务,最后关闭连接。打开和关闭事务中的数据库连接

我就是这么做的在心态“开晚了,早就关门” ...但如果我需要调用其他BOs要在单个事务提交的数据?是否有更好的方式来处理开放和关闭连接以及处理事务?

我是设计应用程序体系结构的新手,所以我希望我不会这样做错误...任何帮助表示赞赏。

回答

5

如果给定的业务对象需要在一个事务中执行各种方法,使用TransactionScope像这样:

using (var transactionScope = new TransactionScope()) 
{ 
    this.Save(); 
    childObjA.Save(); 
    childObjB.Save(); 
    childObjC.Save(); 
    childObjD.Save(); 

    transactionScope.Complete(); 
} 

如果任何物体抛出一个异常,它会回滚事务。

请参阅the MSDN reference page for TransactionScope了解更多信息。

+3

虽然'TransactionScope' * *是这里使用的类,但是对于OP的当前设计,它将导致分布式事务或者在MSDTC被禁用或锁定时可能导致异常。它可能*不是预期的结果。 – Aaronaught 2010-04-02 00:34:39

+1

@Aarounaught,使用SQL Server 2008和.NET 3.5,事务不会被提升为分布式事务(对于当前的设计)。 – 2010-04-02 03:11:30

+2

@Tuzo:确实,在SQL Server 2008中,您可以欺骗多重连接问题**如果**所有连接都在同一个数据库中**并且**只有一个连接同时打开。这仍然是一个相当可疑的做法,尤其是当替代设计更容易构建/维护时。 – Aaronaught 2010-04-02 03:36:56

2

听起来像你有正确的想法。如果需要涉及多个BO,则其中一个需要成为“控制器”,它应该打开和关闭连接,并将其传递给其他人。或者一些“包装器”对象可以处理连接并将其传递给每个BO。您的BO可能需要设计为独立运行(处理自己的连接),并接受来自外部的现有连接。

4

当更高层次的抽象依赖于更低层次的抽象(比如依赖于数据连接的业务逻辑类)时,通常通过构造函数提供更低层次的抽象。该技术被称为构造函数注入

public class OrderService 
{ 
    private SqlConnection connection; 

    public OrderService(SqlConnection connection) 
    { 
     if (connection == null) 
      throw new ArgumentNullException("connection"); 
     this.connection = connection; 
    } 

    // Other methods 
} 

这就允许你写对服务类似下面的代码:

using (TransactionScope tsc = new TransactionScope()) 
using (SqlConnection connection = new SqlConnection(...)) 
{ 
    connection.Open(); 
    OrderService os = new OrderService(connection); 
    os.ProcessOrder(myOrder); 
    ShippingService ss = new ShippingService(connection); 
    ss.ShipOrder(myOrder); 
    tsc.Complete(); 
} 

这是最有可能会是你想要的,在最终 - 在许多服务之间共享一个连接的能力。

这也有助于解耦您的服务来自数据连接的实现细节。这样,如果您想在某些情况下执行某些操作(例如更改连接设置),则无需深入研究50种不同服务的细节,只需更改创建连接的一行代码即可。

还有一件事:如果打算使用TransactionScope,请确保将Transaction Binding=Explicit Unbind添加到连接字符串中,否则如果事务超时,实际上可能会导致数据不一致。

+1

根据msdn的说法,“从.NET Framework版本4开始,对隐式解除绑定的更改会使明确解除绑定过时。” http://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqlconnection.connectionstring(v=vs.100).aspx 很好的回答和例子! – xkingpin 2012-03-22 15:41:12

2

正如别人所说,TransactionScope是要走的路。

如果您使用的是SQL Server 2008和.NET 3。5,我将修改设计以使业务对象控制事务,并将连接的打开和关闭留给数据层。

在连接池打开的情况下,实际上并不会导致打开物理数据库连接的开销,并且只有在执行实际工作时才会打开连接。既然(我认为)你有SQL Server 2008 with .NET 3.5 your transaction will not escalate to a distributed transaction(除非你同时打开多个连接),所以你可以得到两全其美的好处。

然后,你可以写你的业务对象是这样的:

using (TransactionScope transactionScope = new TransactionScope()) 
{ 
    DataObject dataObject = new DataObject(); 
    dataObject.UpdateQuantity(...); 

    ShippingManager shippingManager = new ShippingManager(); 
    shippingManager.ShipOrder(...); 

    transactionScope.Complete() 
} 

这避免了传递连接字符串的所有业务对象和协调使得交易容易。

更新

System.Transactions中的美妙之处,所有的交易都为你而不管您正在使用的连接管理。您只需声明一个TransactionScope,并且该TransactionScope中的所有数据库访问都将以单个事务进行(除非您以其他方式请求TransactionScope设置)。在过去(SQL Server 2005 .NET 2.0),如果您打开和关闭连接,然后打开并关闭另一个连接(即使使用相同的连接字符串),那么该事务将从轻量事务提升为分布式事务交易。这是不可取的,因为性能会受到影响(与MSDTC的通信超出过程并且两阶段落实协议),而MSDTC在很多生产环境(防火墙和安全性)中配置都很麻烦。

使用SQL Server 2008和.NET 3.5,他们添加了在单个事务中打开和关闭具有相同连接字符串的多个连接时避免此升级的功能。对于他们所做的事情的一个很好的解释见Extending Lightweight Transactions in SqlClient

更新2

交易从Oracle 10g将与TransactionScope的正常运行。它看起来像ODP.NET supports Lightweight Transactions(这很好)。不幸的是,我认为推广到分布式交易将在关闭和开放连接时发生。

如果您希望避免分布式事务,可以将连接传递给每个方法调用/业务对象。如果你不想传递连接,你可以使用一个ConnectionScope class来保持连接在线程上打开。另一种方法是使用企业库3.0(及以上)数据访问应用程序块。 Data Access Block can detect that a transaction is in progress and use the same connection避免分布式事务。

+0

如果你有数据层句柄开关连接,你如何使用事务?如果你有三个需要做某事的BO,他们都在同一个事务上,他们是不是都需要使用同一个连接? – 2010-04-02 20:13:37

+0

我正在考虑在企业库中使用数据访问块。我只是不确定是否有足够的时间来重写我的应用程序的这一部分。我将不得不使用EL或将连接传递给每个BO。 我的BO和实体是一样的,所以我可能需要将它们分开以使这项工作更好。 – 2010-04-03 14:51:48

1

您可能正在寻找Unit of Work模式和Registry模式。这两种模式可以协同工作,以分离查找业务对象和跟踪它们的问题,以便稍后将其作为事务提交给数据存储。

我也会关注对象关系映射或ORM。 ORM是工作单元,注册表,持久性无知和其他模式的更高级别组合,它提供了业务逻辑与持久性逻辑之间非常干净的分离。使用和ORM,通常可以消除编写存储过程,构建自定义DAL等的需求.ORM负责为您提供持久性问题,使您能够专注于需要完成的业务逻辑。由于您使用的是C#和.NET,因此我会考虑Entity Framework(v4,不要使用v1)或LINQ to SQL。两者都是从v3.5及之后的.NET框架来的OR映射器。 LINQ to SQL是一个非常简单并且非常好的ORM,它可以让你快速进入。实体框架是一个非常丰富的ORM,它也非常好(比LINQ to SQL更好),并且提供了更多的功能。还有第三方的ORM可以完成这项工作,包括一个名为NHibernate的免费项目。虽然它不如Microsoft ORM那么好,但NHibernate是一个非常成熟的开源ORM,它有一个大型的社区。

如果一个ORM的可能性也不大,那么我会考虑Unit of WorkRegistry(或仓库),持久性无知,Separation of ConcernsSingle Responsibility,以及其他相关的模式。

相关问题