【问题标题】:C# Design Pattern for Database Helper classes数据库助手类的 C# 设计模式
【发布时间】:2011-02-24 23:19:44
【问题描述】:

我正在设计一个将由数百个客户端调用的 WCF 服务,并且我有一个关于将由数据库查询运行的类的最佳体系结构的问题。今天我只访问 SQL Server,所以我有一个内部调用的静态类来完成创建连接和数据读取器的所有脏活。下面是一个简单的例子:

namespace DBHelper.Utility
{
  public static class SqlDBManager
  {
    public static void RunSql(String pSql, DBParamsHelper pDBParams, String pConnStringConfigName)
    {
      String sConnectionString = GetConnectionStringFromConfig(pConnStringConfigName);
      SqlConnection oConn = new SqlConnectionsConnectionString 
      oConn.Open();
      try
      {
        SqlCommand oCommand = new SqlCommand(pSql, oConn);
        oCommand.CommandTimeout = 0;
        if (pDBManagerParams != null)
        {
          foreach (SqlParameter sqlParam in pDBManagerParams)
          {
            oCommand.Parameters.Add(sqlParam);
          }
        }
        oCommand.ExecuteNonQuery();
      }
      finally
      {
        oConn.Close();
      }
    }
  }
}

现在,我需要添加对同时运行 Sql Server 和 Oracle 的支持。我最初的想法是声明一个接口,并让我现有的SqlDBManager 实现它,然后开发一个实现相同接口的OracleDBManager。问题是我的类是静态的,静态类不能实现接口。我希望我的助手类保持静态,因为它更实用,而且我不必每次需要运行查询时都创建一个新对象。我也想过使用类继承,但是我不能有statis虚方法,所以用处不大。我考虑了一些单例实现,所以我不必创建类,但是我在多线程访问时会遇到麻烦。
什么是最好的设计模式,这样我就可以在多线程场景中获得出色的性能(非常重要),无需过多的工作编码以提高生产力(不必创建很多类),并为OracleDBManager 提供标准方法和SqlDBManager 课程?标准方法很重要,因为我不想让使用这些帮助类的代码知道它们是连接到 Oracle 还是 Sql Server。
我确实考虑过使用 ORM 解决方案,例如 Entity Framework 4 和 nHibernate,但是对性能的影响太大了。由于我将运行简单查询,因此 PL-SQL 和 TSQL 之间的查询语法差异并不重要。
任何输入和想法将不胜感激。提示

【问题讨论】:

    标签: c# design-patterns


    【解决方案1】:

    为什么不让你的静态方法private,将类包装在一个接口中以支持MS-SQL / Oracle,并在各自的接口中调用私有静态方法?

    例如:

    public interface ISqlDbManager
    {
       void SaveOrder(Order o);
       void FindOrderById(int orderId);
    }
    
    public class SqlServerDbManager : ISqlDbManager
    {
        private static void RunSql(String pSql, DBParamsHelper pDBParams, String   pConnStringConfigName)
        {
           // implement as you did above
        }
    
       public void FindOrderById(int orderId)
       {
          // create SQL, call private "RunSql" method.
       }
    }
    

    对其他实现 (OracleDbManager) 执行相同的操作。

    将其私有化是有意义的,因为消费者不应该关心底层持久性机制是如何工作的。

    这也将使单元测试更容易 - 创建一个“MockDbManager”类,其中私有静态方法对内存列表执行基本的 LINQ 操作。

    附带说明,我会强烈建议使用存储过程,而不是手动构建 sql 命令。更适合查询计划缓存/优化。

    【讨论】:

    • 我完全同意 procs 的建议。但是,我需要为每个业务对象重复数据库代码,对吧?
    • @Pascal - 正确。那有什么问题?每个对象的一组 CRUD 过程。这样你就可以将它们分开。 ATM,每个对象都使用一种方法。
    • @RPM194 现在我明白你的意思了.. 但是我的类是帮助类,它集中了无聊的东西来实际运行过程/查询。我非常感谢您的意见,以及您为您的建议投入的时间,这是一个好主意,但它不符合我目前的需要。不过谢谢。
    【解决方案2】:

    接口是正确的方向,但正如您所指出的,您不能让静态类实现接口。我理解希望尽量减少创建对象的麻烦,但为了拥有两个不同的数据库类,这可能在某种程度上是必要的。

    我建议的解决方案是多方面的。首先是一个与您上面列出的签名类似的接口:

    public Interface IDbManager {
         void RunSql(String pSql, DBParamsHelper pDBParams, String pConnStringConfigName)
    }
    

    这可以在特定于 SQL 和 Oracle 的版本中实现,您已经拥有 SQL 版本,只需将其设为非静态并实现接口即可。

    现在试试数据库工厂,可能像下面这样:

    public static class DbFactory {
        public static IDbManager CreateDb(DbType type) {
            select (type) {
                case DbType.Sql:
                    return new SqlDbManager();
                    break;
                case DbType.Sql:
                    return new OracleDbManager();
                    break;
            }
        }
    }
    

    那么你应该可以做这样的事情:

    var db = DbFactory.CreateDb(DbType.Sql);
    db.RunQuery(...);
    

    此代码未经测试,但希望您能理解。我在我的一个项目中使用了类似的解决方案,我需要从不同的数据存储中获取数据。策略和工厂模式简化了这个过程。

    希望有帮助!

    【讨论】:

    • 格兰特,谢谢你的建议。这正是我的想法,带有工厂类和枚举器。只是想避免创建对象,但真的不确定是否有办法...... Tks!
    • 我认为避免创建对象的唯一方法是坚持使用单个静态类,并在每次需要创建 DB 对象时使用 DB 类型的开关。我最初是在上述项目中这样做的——太麻烦了!然后,我将数据访问更改为我刚刚介绍的内容;它实际上要简单得多,并且可以为不同的数据提供者提供专业课程。
    猜你喜欢
    • 2010-09-10
    • 1970-01-01
    • 1970-01-01
    • 2011-07-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-07-01
    • 1970-01-01
    相关资源
    最近更新 更多