0

好的,这是我遇到的一个问题。

我的应用程序中有一些类具有需要数据库连接的方法。我在设计类的两种不同方法之间纠结,这两种方法都以依赖注入为中心:

  1. 为调用者在方法调用之前设置的连接提供一个属性。这有一些缺点。

    • 依赖连接属性的每个方法都必须验证该属性以确保它不为空,它是打开的并且不参与事务,如果这会破坏操作。

    • 如果连接属性意外关闭,所有方法都必须(1.)抛出异常或(2.)强制打开。根据您想要的稳健性级别,任何一种情况都是合适的。(请注意,这与传递给方法的连接不同,因为对连接的引用存在于对象的生命周期中,而不仅仅是在方法调用的生命周期中。因此,连接的波动性似乎更高对我来说。)

    • 提供一个Connection属性似乎(对我来说,无论如何)为相应的Transaction属性尖叫。这会在文档中产生额外的开销,因为您必须在使用事务以及何时不使用事务时使其相当明显。

    另一方面,微软似乎更喜欢整个集合调用范式。

  2. 要求将连接作为参数传递给方法。这有几个优点和缺点:

    • 参数列表自然更大。这对我来说很烦人,主要是在打电话的时候。

    • 虽然连接(和事务)在使用前仍必须经过验证,但对它的引用仅在方法调用期间存在。

    • 然而,呼叫点非常明确。很明显,必须提供连接,并且该方法不会在您背后自动创建连接。

    • 如果一个方法不需要事务(比如只从数据库中检索数据的方法),则不需要事务。由于方法签名,不乏清晰度。

    • 如果一个方法需要一个事务,由于方法签名,它是非常清楚的。同样,不乏明确性。

    • 因为该类没有公开一个Connection或一个Transaction属性,所以调用者没有机会尝试深入了解它们的属性和方法,从而执行得墨忒耳法则。

我知道,很多。但一方面是微软的方式:提供属性,让调用者设置属性,然后调用方法。这样,您不必创建复杂的构造函数或工厂方法等。另外,避免使用带有很多参数的方法。

然后,一个简单的事实是,如果我在我的对象上公开这两个属性,它们往往会鼓励消费者以邪恶的方式使用它们。(不是说我对此负责,但仍然。)但我只是不想写蹩脚的代码。

如果你在我的鞋子里,你会怎么做?

4

2 回答 2

1

这是要考虑的第三种模式:

  • 创建一个名为 ConnectionScope 的类,它提供对连接的访问
  • 任何时候任何类,都可以创建一个ConnectionScope
  • ConnectionScope 有一个名为 Connection 的属性,它总是返回一个有效的连接
  • 任何(和每个)ConnectionScope 都可以访问相同的底层连接对象(在某个范围内,可能在同一个线程或进程内)

然后,您可以随意实现该 Connection 属性,并且您的类没有需要设置的属性,连接也不是参数,也不需要担心打开或关闭连接。

更多细节:

  • 在 C# 中,我建议 ConnectionScope 实现 IDisposable,这样您的类可以编写类似“使用(var scope = new ConnectionScope())”的代码,然后 ConnectionScope 可以在连接被销毁时释放连接(如果合适)
  • 如果您可以将自己限制为每个线程(或进程)一个连接,那么您可以轻松地在 ConnectionScope 的 [thread] 静态变量中设置连接字符串
  • 然后,您可以使用引用计数来确保您的单个连接在它已经打开时被重新使用,并且在没有人使用它们时释放连接

更新:这是一些简化的示例代码:

public class ConnectionScope : IDisposable
{
   private static Connection m_Connection;
   private static int m_ReferenceCount;

   public Connection Connection
   {
      get
      {
          return m_Connection;
      }
   }

   public ConnectionScope()
   {
      if ( m_Connection == null )
      {
          m_Connection = OpenConnection();
      }
      m_ReferenceCount++;
   }

   public void Dispose()
   {
      m_ReferenceCount--;
      if ( m_ReferenceCount == 0 )
      {
         m_Connection.Dispose();
         m_Connection = null;
      }
   }
}

您的一个(任何)类如何使用它的示例代码:

using ( var scope = new ConnectionScope() )
{
   scope.Connection.ExecuteCommand( ... )
}
于 2009-07-21T02:11:23.923 回答
0

我更喜欢后一种方法。听起来您的类使用数据库连接作为持久层的管道。让调用者传入数据库连接可以清楚地表明情况就是这样。如果连接/事务被表示为对象的属性,那么事情就不那么清楚了,所有的所有权和生命周期问题都会出现。最好从一开始就避免它们。

于 2009-07-21T02:10:31.473 回答