处理另一个函数的异常

时间:2014-01-07 10:13:24

标签: c# .net exception sqlconnection

我想生成一个打开SQL连接的方法:

public SqlConnection openConnection (string connString)
{
    using (SqlConnection conn = new SqlConnection(connString))
    {
        try
        {
            conn.Open();
        }
        catch (Exception)
        {
           OrdersFaultException connEx = new OrdersDBInternalFaultException();
           throw new FaultException<OrdersFaultException>(connEx);
           return null;
        }
        return conn;
    }
}

我想知道上面的代码是否正确?或者我应该在openConnection中抛出异常,并在调用openConnection的函数中处理异常?

4 个答案:

答案 0 :(得分:2)

您应该处理相同抽象级别的异常,因为代码会引发这些异常。

这意味着,负责打开数据库的代码可能会抛出与数据库相关的异常,负责处理订单的代码可能会抛出与订单相关的异常。

您可能希望从 openConnection 方法中抛出类似 DatabaseConnectionException (带有相应的错误消息)的内容,然后引发 OrdersDBInternalFaultException 在发生 DatabaseConnectionException 的情况下,您需要处理 Orders 的地方。

附注:抛出异常后,您不需要返回null 。永远不会达到这种回报,这是一个死代码。

答案 1 :(得分:2)

您不能在此处使用 :没有人想要处置连接。 所以你可以使用模拟:

public SqlConnection openConnection (string connString) {
  SqlConnection conn = null;

  try {
    conn = new SqlConnection(connString));

    return conn;
  }
  catch (Exception e) { // <- Probably, you should catch more specific exception, e.g. DataException
    // This "if" (that's a part of typical using emulation scheme) is redundant
    // you may safely remove it unless there's no addition code in the "try"
    // see Todd Bowles comments
    if (!Object.ReferenceEquals(null, conn))
      conn.Dispose();

    // Do not forget to pass the reason (intitial exception e)
    // when throwing your own one
    throw new OrdersDBInternalFaultException(e);    
  }
}

答案 2 :(得分:1)

德米特里和蒂姆都是正确的。

对于异常处理虽然在您尝试打开连接的情况下,因此如果未实现所需的功能,则应抛出异常。因此,该用户知道函数必须执行的功能尚未执行。

答案 3 :(得分:1)

回到C ++时代,异常安全确实非常重要。如果我没记错的话,Sutter的Exceptional C ++在很多细节上对此进行了描述。

现在可能会让人感到奇怪的一件事就是C ++中没有“终极”。如果在堆栈上添加create对象,则在退出作用域时它将自动“处理”。说白了,范围是'{'和'}'为你管理的东西。

如果您正在使用C#,异常处理仍然很重要,但在不同的级别上。在所有情况下,您必须确保整体状态保持一致。那么这是如何工作的?

using只不过是trycatchfinally的包装。它基本上在Dispose中创建finally,从而确保当您退出范围(无论出于何种原因)时,对象将被清除(即,假设它实现IDisposable)。因此,确保正确清理对象的最简单方法是:

  1. 创建实例,
  2. 使用实例
  3. 处理实例
  4. (按此顺序!)

    E.g:

    using (var connection = new Connection())  // create instance
    { 
        // use connection instance here
    
    } // dispose connection instance -> implicitly created by 'using'.
    

    重新使用代码

    如果您不想再重复编写代码,这是一件好事(f.ex.与连接字符串),您可以手动处理对象,也可以使用简单的回调函数来保持流动相同。

    private void Execute(Action<Connection> action) 
    {
        using (var connection = new Connection())  // create instance
        { 
            // use connection instance here
    
            action(connection); // callback; this won't break the described mechanism
    
        } // dispose connection instance -> implicitly created by 'using'.
    }
    

    为什么不正确使用using

    所以这是一个返回连接而不考虑使用的实现:

    // Buggy connection builder
    private Connection CreateConnection()  
    { 
        Connection tmp = new Connection();
        // do some stuff with tmp, might throw
    
        return tmp;
    }
    
    private void Client() 
    {
        using (var connection = CreateConnection())
        {
            // do something with the connection, assuming we're safe
        }
    }
    

    这似乎一切都很好,但实际上是不正确的。你能看到吗?

    在它说“用tmp做一些东西”的部分,有些东西可能出错,导致方法抛出。发生这种情况时,永远不会将对象返回给客户端,只留下没有引用的活动Connection。即使你认为你是安全的,你实际上也不是。

    关于构造函数和异常

    如果你注意了,这也意味着构造者可能会出错。如果你创建一个没有正确清理的对象,如果狗屎击中了风扇,你还有一个悬空物体。例如:

    // Buggy connection wrapper
    public class ConnectionWrapper : IDisposable
    { 
        public ConnectionWrapper() 
        {
            this.connection = new Connection();
            // do some other stuff, some of which might throw
        }
    
        private Connection connection;
        // ...
    
        // IDisposable stuff.
    }
    
    private void Client() 
    {
        using (var connection = new ConnectionWrapper())
        {
            // do something with the connection, assuming we're safe
        }
    }
    

    同样,构造函数中的代码可能会抛出异常。在这种情况下,构造函数不会将对象返回给客户端,这意味着IDisposable中的finally将不会调用using。这并不意味着IDisposable在这里是错误的 - 事实上它不是!

    这里出现的问题是我们创建了一个IDisposable对象,该对象未被返回且未被清除。我们可以通过添加catch来解决此问题,只需在清理连接后重新抛出异常。