数据库助手类的 C# 设计模式

发布于 2024-10-19 06:28:05 字数 1511 浏览 1 评论 0原文

我正在设计一个将由数百个客户端调用的 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虚拟方法,所以没有太多用处。我考虑了一些单例实现,这样我就不必创建类,但是这样我在多线程访问上就会遇到麻烦。
什么是最好的设计模式,这样我就可以在多线程场景中获得出色的性能(非常重要),不需要太多工作编码来提高生产力(不必创建很多类),并且对这两个 有一个标准方法OracleDBManagerSqlDBManager 类?标准方法非常重要,因为我不想让使用这些帮助器类的代码知道它们是否连接到 Oracle 或 Sql Server。
我确实考虑过使用ORM解决方案,例如Entity Framework 4和nHibernate,但性能影响太大。由于我将运行简单查询,因此 PL-SQL 和 TSQL 之间的查询语法差异并不重要。
任何意见和想法将不胜感激。塔克斯

I'm designing a WCF Service that will called by several hundred clients, and I have a question about the best architecture for the classes that will run by database queries. Today I only access SQL Server, so I have a static class that I call internally that does all the dirty work of creating connections and datareaders. Below is a simple example:

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();
      }
    }
  }
}

Now, I need to add support for running both Sql Server and Oracle. My initial idea was to declare an interface, and have my existing SqlDBManager implement it, and then develop an OracleDBManager implementing the same interface. The problem is that my class is static, and static classes cannot implement an interface. I would like my helper class to remain as static, with it's a lot more practical, and I don't have to create a new object every time I need to run a query. I also thought of using class inheritance, but I can't have statis virtual methods, so not much use there. I considered some singleton implementations so I wouldn't have to create classes, but then I would have trouble on the multi-threaded access.
What would be the best design pattern so I can have great performance on multiple threaded scenario (very important), not too much work coding for productivity (not have to create a lot of classes), and have a standard methods for both OracleDBManager and SqlDBManager classes? The standard method is very important, because I don't want to the code that uses these helper classes to know if they are connected to Oracle or Sql Server.
I did consider using ORM solution, such as Entity Framework 4 and nHibernate, but the performance impact was too much. Since I will run simple queries, the query syntax difference between PL-SQL and TSQL won't matter.
Any input and idea will be greatly appreciated. Tks

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

为什么不让你的静态方法私有,将类包装在一个接口中以支持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 命令。更适合查询计划缓存/优化。

Why not make your static method private, wrap the classes in an interface to support MS-SQL / Oracle, and call the private static methods in the respective interfaces?

E.g:

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.
   }
}

Do the same thing for the other implementation (OracleDbManager).

It makes sense to have it private, since the consumer shouldn't care how the underlying persistence mechanism works.

And this will also make unit testing easier - create a "MockDbManager" class, where the private static method does basic LINQ operations on an in-memory list.

On a side note, i would strongly recommend the use of stored procedures, instead of constructing sql commands manually. Better for query plan caching / optimization.

月亮坠入山谷 2024-10-26 06:28:05

接口是正确的方向,但正如您所指出的,您不能让静态类实现接口。我理解希望最大限度地减少对象创建的麻烦,但这可能以某种方式是必要的,以便拥有两个不同的数据库类。

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

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(...);

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

希望有帮助!

The interface is the right direction to go in, but as you've pointed out, you can't have a static class implement an interface. I understand wanting to minimize the fuss of object creation, but that will likely be necessary in some way in order to have two different database classes.

The solution I suggest is multi-faceted. First is an interface with a signature similar to what you listed above:

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

That can be implemented in SQL- and Oracle-specific versions, you already have the SQL version, just make it non-static and implement the interface.

Now try a database factory, perhaps like the following:

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;
        }
    }
}

Then you should be able to do something like:

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

This code is untested, but hopefully you get the idea. I use a similar solution for one of my projects where I need to get data from different data stores. The strategy and factory patterns ease this process.

Hope that helps!

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文