如何在C#三层架构中为两个方法实现Oracle事务
在三层架构中实现跨方法的Oracle事务
我来帮你搞定这个跨方法的Oracle事务问题,刚好之前在三层架构里处理过类似的场景!核心思路是让整个Cancel和Confirm操作共享同一个数据库连接和事务对象,把事务从UI层往下传递到BAL、DAL,确保所有数据库操作都在同一个事务范围内。
具体实现步骤
咱们一步步来拆解:
在UI层(Aspx.cs)初始化事务
你需要在按钮点击事件里创建Oracle连接和事务,因为事务是绑定在连接上的,整个流程必须复用同一个连接。同时用using语句确保资源能正确释放。将事务对象逐层传递
把创建好的OracleTransaction对象作为参数,传递给BAL的Cancel、Confirm方法,再由BAL传递给对应的DAL方法。DAL层复用事务执行操作
DAL里的数据库操作不再单独创建连接,而是使用事务关联的连接,并且把Command的Transaction属性设置为传入的事务对象。统一处理事务的提交/回滚
如果Cancel和Confirm都执行成功,就提交事务;只要其中任何一步抛出异常,就回滚事务,保证数据一致性。
代码示例
1. UI层(Aspx.cs的按钮点击事件)
protected void On_Cancel_Button_Click(object sender, EventArgs e) { // 替换成你的Oracle连接字符串 string connectionString = "Data Source=ORCL;User Id=yourUser;Password=yourPass;"; using (OracleConnection conn = new OracleConnection(connectionString)) { try { conn.Open(); // 开始事务 using (OracleTransaction transaction = conn.BeginTransaction()) { // 实例化BAL类 TicketBAL ticketBAL = new TicketBAL(); // 调用Cancel方法,传入事务 bool cancelSuccess = ticketBAL.CancelBooking(transaction, yourBookingId); if (cancelSuccess) { // 检查是否有候补票,如果有调用Confirm方法 if (HasWaitingList()) { bool confirmSuccess = ticketBAL.ConfirmWaitingBooking(transaction, yourWaitingId); if (!confirmSuccess) { throw new Exception("候补票确认失败"); } } } else { throw new Exception("订票取消失败"); } // 所有操作成功,提交事务 transaction.Commit(); ClientScript.RegisterStartupScript(this.GetType(), "alert", "alert('操作成功');", true); } } catch (Exception ex) { // 任何异常都回滚事务 if (conn.State == ConnectionState.Open && conn.Transaction != null) { conn.Transaction.Rollback(); } ClientScript.RegisterStartupScript(this.GetType(), "alert", $"alert('操作失败:{ex.Message}');", true); } } } // 模拟检查是否有候补票的方法 private bool HasWaitingList() { // 这里实现你的逻辑,比如查询数据库是否存在候补记录 return true; }
2. BAL层(TicketBAL.cs)
public class TicketBAL { private TicketDAL ticketDAL = new TicketDAL(); public bool CancelBooking(OracleTransaction transaction, int bookingId) { // 可以在这里做业务逻辑校验,比如检查订票状态是否允许取消 if (!IsBookingCancelable(bookingId)) { return false; } // 将事务传递给DAL方法 return ticketDAL.CancelBooking(transaction, bookingId); } public bool ConfirmWaitingBooking(OracleTransaction transaction, int waitingId) { // 业务逻辑校验,比如检查候补状态 if (!IsWaitingConfirmable(waitingId)) { return false; } return ticketDAL.ConfirmWaitingBooking(transaction, waitingId); } // 模拟业务校验方法 private bool IsBookingCancelable(int bookingId) { return true; } private bool IsWaitingConfirmable(int waitingId) { return true; } }
3. DAL层(TicketDAL.cs)
public class TicketDAL { public bool CancelBooking(OracleTransaction transaction, int bookingId) { // 使用事务关联的连接,不要自己新建连接 using (OracleCommand cmd = transaction.Connection.CreateCommand()) { cmd.Transaction = transaction; // 绑定事务 cmd.CommandText = "UPDATE BOOKINGS SET STATUS = 'CANCELED' WHERE BOOKING_ID = :BookingId"; cmd.Parameters.Add(":BookingId", OracleDbType.Int32).Value = bookingId; int rowsAffected = cmd.ExecuteNonQuery(); return rowsAffected > 0; } } public bool ConfirmWaitingBooking(OracleTransaction transaction, int waitingId) { using (OracleCommand cmd = transaction.Connection.CreateCommand()) { cmd.Transaction = transaction; cmd.CommandText = @"UPDATE WAITING_LIST SET STATUS = 'CONFIRMED' WHERE WAITING_ID = :WaitingId; INSERT INTO BOOKINGS (USER_ID, FLIGHT_ID) SELECT USER_ID, FLIGHT_ID FROM WAITING_LIST WHERE WAITING_ID = :WaitingId"; cmd.Parameters.Add(":WaitingId", OracleDbType.Int32).Value = waitingId; // 执行多语句 int rowsAffected = cmd.ExecuteNonQuery(); return rowsAffected > 0; } } }
关键注意事项
- 不要在DAL中单独打开新连接:所有数据库操作必须复用UI层创建的连接,否则事务无法覆盖。
- 事务范围要尽量小:不要在事务中做耗时操作(比如调用外部接口、大量计算),避免长时间持有数据库锁,影响性能。
- 异常处理要全面:确保任何异常都能触发回滚,包括业务逻辑校验失败的情况(可以主动抛出异常来触发回滚)。
- 使用using管理资源:连接、事务、Command都用using包裹,确保即使出错也能释放资源。
内容的提问来源于stack exchange,提问作者Shahnawaz Ahmad
相关产品推荐
相关产品推荐

