开门见山,使用MS Enterprise Library的DAAB(Data Access Application Block)获取数据时抛出异常。具体场景如下,通过Database对象的ExecuteReader执行两段Select语句,前一句是不合法的,后一句是正确的。为了避免第一次执行出错导致程序的终止,特意将其放到Try/Catch酷快中。两次数据库操作通过TrsanctionScope的形式纳入同一个Transaction中,具体的代码如下所示。
1: class Program<!--CRLF-->
2: {
<!--CRLF-->3: static void Main()<!--CRLF-->
4: {
<!--CRLF--> 5:
<!--CRLF-->6: string invalidSql = "SELECT * FROM {InvalidTable}";<!--CRLF-->
7: string validSql = "SELECT * FROM {ValidTable}";<!--CRLF-->
8:
<!--CRLF--> 9:
<!--CRLF--> 10: Database db = DatabaseFactory.CreateDatabase();
<!--CRLF-->11: using (TransactionScope scope = new TransactionScope())<!--CRLF-->
12: {
<!--CRLF--> 13: DbCommand commandWithInvalidSql = db.GetSqlStringCommand(invalidSql);
<!--CRLF--> 14: DbCommand commandWithValidSql = db.GetSqlStringCommand(validSql);
<!--CRLF--> 15:
<!--CRLF-->16: try<!--CRLF-->
17: {
<!--CRLF--> 18: db.ExecuteReader(commandWithInvalidSql);
<!--CRLF--> 19: }
<!--CRLF-->20: catch<!--CRLF-->
21: { }
<!--CRLF--> 22:
<!--CRLF--> 23: db.ExecuteReader(commandWithValidSql);
<!--CRLF--> 24: }
<!--CRLF--> 25: }
<!--CRLF--> 26: }
<!--CRLF-->
但是在执行第二个ExecuteReader方法的时候却抛出如下一个InvalidOperationException(如下图),错误消息为:“ExecuteReader requires an open and available Connection. The connection's current state is closed.”
原因出在这里:在ExecuteReader中,相应的ADO.NET代码放在try|catch中,当异常抛出后,相应的DbConnect会被关闭。但是由于在我的代码中,两次ExecuteReader的调用是在一个相同的Ambient Transaction中执行的,DAAB在内部采用相同的DbTransaction执行这两项操作,当执行第一项操作时,由于出现异常导致DbConnect关闭,使用相同DbConnect的第二项操作肯定会失败。
1: public virtual IDataReader ExecuteReader(DbCommand command)<!--CRLF-->
2: {
<!--CRLF-->3: ConnectionWrapper wrapper = GetOpenConnection(false);<!--CRLF-->
4:
<!--CRLF-->5: try<!--CRLF-->
6: {
<!--CRLF-->7: //<!--CRLF-->
8: // JS-L: I moved the PrepareCommand inside the try because it can fail.<!--CRLF-->
9: //<!--CRLF-->
10: PrepareCommand(command, wrapper.Connection);
<!--CRLF--> 11:
<!--CRLF-->12: //<!--CRLF-->
13: // If there is a current transaction, we'll be using a shared connection, so we don't<!--CRLF-->
14: // want to close the connection when we're done with the reader.<!--CRLF-->
15: //<!--CRLF-->
16: if (Transaction.Current != null)<!--CRLF-->
17: return DoExecuteReader(command, CommandBehavior.Default);<!--CRLF-->
18: else<!--CRLF-->
19: return DoExecuteReader(command, CommandBehavior.CloseConnection);<!--CRLF-->
20: }
<!--CRLF-->21: catch<!--CRLF-->
22: {
<!--CRLF--> 23: wrapper.Connection.Close();
<!--CRLF-->24: throw;<!--CRLF-->
25: }
<!--CRLF--> 26: }
<!--CRLF--> 27:
<!--CRLF-->
我不清楚微软在设计的时候,是因为没有考虑到这种场景呢,还是不得以而为之,或者是出于其他因素的考虑,大家有何见解。