【问题标题】:Keeping an application database agnostic (ADO.NET vs encapsulating DB logic)保持应用程序数据库不可知(ADO.NET 与封装数据库逻辑)
【发布时间】:2011-03-06 01:37:53
【问题描述】:

我们正在制作一个相当严肃的应用程序,它需要与客户想要使用的数据库保持不可知性。最初我们计划支持 MySQL、Oracle 和 SQL Server。表和视图和查询一样简单(没有真正花哨的 SQL),因此问题是:

  1. 使用原生数据库驱动(MySQLDbConnection 等),封装执行查询和处理结果的逻辑或
  2. 使用通用 OleDbConnection

显然选项 2 不涉及任何开销,但我认为性能不如原生访问?

【问题讨论】:

标签: c# mysql database oracle oledb


【解决方案1】:

注意:如果您决定使用基本的 ADO.NET 2 功能而不是 ORM(例如 Entity Framework 或 NHibernate)或 LINQ to SQL,则此答案是相关的。

假设您在app.config 中定义了一个连接字符串:

<connectionStrings>
    <add name="SomeConnection"
         providerName="System.Data.SqlClient"
         connectionString="..." />
</connectionStrings>

注意providerName 属性及其值的存在。您还可以为另一个数据库提供程序输入一个值,例如System.Data.SQLite.

(请注意,非标准提供程序,即默认情况下不在 .NET Framework 中的提供程序,need to be registered 首先是 app.config 或客户端计算机的 machine.config。)

现在,您可以以完全与提供者无关的方式使用指定的数据库,如下所示:

using System.Configuration;  // for ConfigurationManager
using System.Data;           // for all interface types
using System.Data.Common;    // for DbProviderFactories

var cs = ConfigurationManager.ConnectionStrings["SomeConnection"];
//                                              ^^^^^^^^^^^^^^^^

var factory = DbProviderFactories.GetFactory(cs.ProviderName);
//                                           ^^^^^^^^^^^^^^^

using (IDbConnection connection = factory.CreateConnection())
{
    connection.ConnectionString = cs.ConnectionString;
    //                            ^^^^^^^^^^^^^^^^^^^
    connection.Open();
    try
    {
        using (IDbCommand command = connection.CreateCommand())
        {
            ...  // do something with the database
        }
    }
    finally
    {
        connection.Close();
    }
}

请注意此代码如何仅适用于接口类型。指定特定数据库提供程序的唯一位置是通过app.config 文件中的providerName 属性值。 (我已经用^^^s 标记了所有从app.config 进行设置的地方。)


延伸阅读:

【讨论】:

  • PS:这个答案没有涵盖的是参数化命令。 .NET Framework 不提供与 DB 提供者无关的方式来引用 SQL CommandText 中的命令参数。
  • 我在同一个应用程序中使用 SQL Server 和 Oracle 的方法如下: DbParameter p = factory.CreateParameter(); p.ParameterName = "toDateTime"; p.DbType = DbType.日期; p.Value = toDateTime; command.Parameters.Add(p);
  • @Thomas,你是对的,但是你将如何在实际命令中引用参数(IDbCommand.CommandText)?应该是@toDateTime(命名参数)吗?还是您使用? 令牌(位置参数)?还是其他一些语法? ADO.NET 并没有真正为此提供独立于提供商的解决方案。
  • 好点。当我之前完成此操作时(对于 Oracle、SQLite 和 SQL Server),我已经对查询文本进行了字符串替换,将 SQL Server 的 ':' 转换为 '@'。它并不优雅,但它有效。不幸的是,由于其他 SQL 语法差异,它变得难以管理,因此 SQL 必须保持在最低公分母级别。最好使用 ORM,显然 :)
  • ... 或质疑您是否真的需要您的代码独立于数据库/不可知论。您实际上多久将生产数据库迁移到不同的 RDBMS?也许不经常。
【解决方案2】:

恕我直言,使用ORM 是一个很好的设计决策,以便拥有一个与数据库无关的应用程序。切换数据库可能就像更改配置设置和连接字符串一样简单。

【讨论】:

  • 有点同意,但 ORM 有自己的问题,首先是性能。
  • @Cylon Cat,在大型应用程序中,就像这里的情况一样,ORM 产生的性能损失与其他部分相比非常小,并且比使用单一语言表达一切的优势要重要,可维护性、数据库不可知论、填补关系世界和面向对象世界之间的空白……
  • 作为概括,我同意,但与所有事情一样,性能损失不会在应用程序中均匀分布。某些功能可能特别复杂,或者对性能特别敏感,这有助于准确了解 ORM 如何以及在何处遇到问题。原型和基本性能测试和分析在这里非常有用。
【解决方案3】:

您不需要OleDbConnection 来访问非特定的 ADO.NET 提供程序。只需使用DbConnection 等。人。请参阅DbProviderFactories on MSDN 了解更多信息。

【讨论】:

    【解决方案4】:

    通过将 Oracle 包括在该列表中,您可以保证没有什么是简单的。

    • 与使用“at”符号的 SQL Server 相比,Oracle 对参数使用不同的前缀字符(冒号)。
    • Oracle 对 long、int、short、boolean、float 和 decimal 使用单一数据类型(数字);您的代码必须确保正确映射它们。
    • 您必须参数化 Oracle 日期和时间值;如果您尝试在 SQL 语句中使用字符串作为日期,您会因为 Oracle 的日期格式而发疯。 (Oracle 使用三个字符的月份缩写;格式为 01-JAN-2010。)
    • 处理空值的基本 SQL 函数可能不同,尤其是空值合并。 (“NVL”与“COALESCE”)Oracle 对保留字更加挑剔。
    • Oracle 不支持本机标识列。变通方法涉及序列、触发器和要求事务,只是为了从新行中检索身份值。

    换句话说,您的应用不能与 DB 无关。如果您不使用 ORM,您肯定会想要构建一个数据访问层,以便对应用程序的其余部分隐藏所有这些内容。

    这里的经验之声。只是在说'。对于跨 SQL Server 和 Oracle 的通用架构,我们必须构建 ORM 的大部分基础架构,同时避免可能降低性能的方面。有趣,但不平凡,绝对!

    【讨论】:

    • Oracle 不使用三个字符的月份缩写。日期/时间在内部为七个字节。当您转换为字符格式时,如果您不指定,它将是默认格式掩码。您可以将默认格式掩码设置为您想要的任何内容。
    • PS Oracle 支持 COALESCE。 Oracle 与其他 RDBMS 供应商在 NULL 方面的最大区别在于 Oracle 不支持零长度、非空字符串。聚苯乙烯。投票赞成,因为我同意 DB 不可知论并不容易
    • @Gary,很高兴知道,谢谢。当我进入项目时,这部分迁移已经完成。
    【解决方案5】:

    LINQ 是一个备受推崇的 .NET ORM,部分原因是您可以使用它和存储过程。问题是,它只是 SQL Server,但人们正在努力为 OracleMySQL 提供类似的功能。

    对于数据库和查询优化,我对使用 ORM 的想法感到畏惧。数据类型、函数和整体语法在 SQL 中不是很便携。与每个数据库交互的最高效方式是为每个数据库定制模型和查询,但这意味着专业知识、时间和金钱。如果需要,请专注于具有代码设置的一个数据库供应商,以支持供应商换出并根据需要添加对其他数据库的支持。

    【讨论】:

    • LINQ to Entities 可以支持其他数据库。我相信它可以在运行时支持任何 ADO.NET 数据提供程序,并且对非 SQLServer 提供程序的唯一限制是在设计时。我在 SQLite 上非常高兴地使用它;它只有几个设计时的怪癖。
    【解决方案6】:

    没有充分的理由避免使用具有最广泛支持的最通用接口 - OleDb 甚至 ODBC,如果您对它们感到满意的话。除此之外的任何事情都会减少您可以使用的产品/语言/平台/工具/开发人员的数量。由于最接近 SQL 金属,供应商不会引入太多低效率 - 肯定少于更深奥的选项。他们已经存在了很长时间来解决任何问题。

    如果您要添加一个抽象层(您自己的或其他人的),那么应该根据在您的特定上下文中引入的抽象的优点来决定,而不仅仅是拥有一个抽象层(这只是更多的支持,除非有故意的好处。)

    如您所见,每个人的里程都不同。 :) 但总的来说,我认为越简单越好。

    【讨论】:

      【解决方案7】:

      为什么不使用 Microsoft 模式和实践 Enterprise Library 数据访问应用程序块。开销极小,切换供应商也很容易。

      引用:

      数据访问应用程序块 利用这些课程和 提供了一个模型,进一步支持 数据库封装 类型——特定的特征,例如 参数发现和类型 转换。因为这, 应用程序可以从一个移植 数据库类型到另一个没有 修改客户端代码。

      【讨论】:

        【解决方案8】:

        您始终可以通过让大部分应用程序将 DAL 用作一堆接口来使应用程序数据库的一部分不可知。然后,DAL 本身将为目标数据库提供具体的实现。

        这样,您可以在 DAL 的使用中解耦,但可以从 DAL 中的性能改进或供应商特定构造中受益。

        【讨论】:

          猜你喜欢
          • 2013-04-12
          • 1970-01-01
          • 1970-01-01
          • 2017-11-05
          • 1970-01-01
          • 2011-02-25
          • 1970-01-01
          • 1970-01-01
          • 2020-09-19
          相关资源
          最近更新 更多