【问题标题】:Parameter naming using System.Data.Common使用 System.Data.Common 的参数命名
【发布时间】:2009-08-11 16:09:52
【问题描述】:

这可能是老歌,但很好。我将 System.Data.Common 用于可互换的 Oracle/SQL Server/SQLite 数据访问库。在构造函数期间,我获取连接字符串名称并使用它来确定底层提供程序类型。我这样做的原因是为每个提供者处理不同的 IDbParameter 命名约定。例如,Oracle 喜欢 :parameter,而 SQL Server 和 SQLite 喜欢 @parameter。默认值为 ?覆盖 Oledb。

问题:这一切都是不必要的吗?我是否缺少一些简单的东西来解决这个问题?如果我的 IDbCommand.CommandText = "select id, name from my.table where id = :id" 我被覆盖了吗?现在我只是采用?作为默认值,然后在执行命令之前使用正则表达式找到正确的参数标识符。

谢谢。

        /// <summary>
    /// Initializes a new instance of the <see cref="RelationalGateway"/> class.
    /// </summary>
    /// <remarks>You must pass in the name of the connection string from the application configuration
    /// file rather than the connection string itself so that the class can determine
    /// which data provider to use, e.g., SqlClient vs. OracleClient.</remarks>
    public RelationalGateway(string connectionStringName)
    {
        if (string.IsNullOrEmpty(connectionStringName)) throw new ArgumentNullException("connectionStringName");
        if (ConfigurationManager.ConnectionStrings[connectionStringName] == null ||
            ConfigurationManager.ConnectionStrings[connectionStringName].ConnectionString.Length == 0 ||
            ConfigurationManager.ConnectionStrings[connectionStringName].ProviderName.Length == 0)
        {
            throw new InvalidOperationException(string.Format(
                                                    "The configuration file does not contain the {0} connection ",
                                                    connectionStringName) +
                                                "string configuration section or the section contains empty values. Please ensure the " +
                                                "configuration file has the appropriate values and try again.");
        }

        _connectionString = ConfigurationManager.ConnectionStrings[connectionStringName].ConnectionString;
        _providerName = ConfigurationManager.ConnectionStrings[connectionStringName].ProviderName;
        _theProvider = DbProviderFactories.GetFactory(_providerName);
        _adapter = _theProvider.CreateDataAdapter();
        //GetConnection();
        DetermineProviderSpecificParameters();
    }

DetermineProviderSpecificParameters 位基本上算出“?”或“:”或“@”或其他。

更新 以下是我目前处理细节的方式:

  1. 获取正确的参数字符串:

    私人无效的DefineProviderSpecificParameters() { // 检查支持的提供者。这是为了限制参数化查询 // 按空间范围正确创建。 字符串短名称 = _providerName.Substring(_providerName.LastIndexOf(".") + 1);

        switch (shortName)
        {
            case "SqlClient":
                _param = "@";
                _ql = "[";
                _qr = "]";
                break;
            case "SQLite":
                _param = "@";
                _ql = string.Empty;
                _qr = string.Empty;
                break;
            case "OracleClient":
                _param = ":";
                _ql = string.Empty;
                _qr = string.Empty;
                break;
            default:
                _param = "?";
                _ql = string.Empty;
                _qr = string.Empty;
                break;
        }
    }
    
  2. 在我执行每个命令之前调用一个小助手来“清理”或“参数化”它,或者我们称之为半途而废的黑客:

    private void MakeProviderSpecific(IDbCommand command)
    {
        foreach (IDataParameter param in command.Parameters)
        {
            param.ParameterName = GetProviderSpecificCommandText(param.ParameterName);
        }
        command.CommandText = GetProviderSpecificCommandText(command.CommandText);
    }
    
  3. 这需要一点正则表达式来做:

    public string GetProviderSpecificCommandText(string rawCommandText)
    {
        return Regex.Replace(rawCommandText, @"\B\?\w+", new MatchEvaluator(SpecificParam));
    }
    

哎呀。仍在寻找一个相对简单的解决方案,但到目前为止的建议肯定是值得赞赏的。

【问题讨论】:

  • 不确定,但可以选择 NHibernate。
  • 我正在将 NHibernate 用于其他项目,但这是我的其他开发人员使用的实用程序库,它为他们提供了一种快速但不太脏的 Db 访问方法。另外,我将其用作反开源客户的代码核心。是的,我手卷数据访问代码的次数超出了我的想象! :-(

标签: c# database


【解决方案1】:

我为Salamanca 做了类似的事情:见ParameterBuilder.cs。此代码使用:

问题是您需要一个有效的参数名称(Sql Server 中的"@name",Oracle 中的"name"),以及 SQL 查询中的有效占位符(Sql Server 中的"@name",Sql Server 中的":name"甲骨文)。

  1. 如果连接正确,GetParameterName 将为您的参数提供一个有效名称。
  2. 创建你的占位符:

    • 通过GetParameterPlaceholder
    • 或者查询the schema for your connection中包含的DbMetaDataColumnNames.ParameterMarkerFormat值。您应该能够通过将此字符串用作格式字符串来创建占位符,将前面的参数名称作为输入(暗示格式字符串对于 Sql Server 是 "{0}",对于 Oracle 是 ":{0}"):

      // DbConnection connection;
      // string parameterName
      DataRow schema=connection.GetSchema(DbMetaDataCollectionNames.DataSourceInformation).Rows[0];
      string placeholder=string.Format(
          CultureInfo.InvariantCulture,
          (string)schema[DbMetaDataColumnNames.ParameterMarkerFormat],
          name.Substring(0, Math.Min(parameterName.Length, (int)schema[DbMetaDataColumnNames.ParameterNameMaxLength]))
      );
      

这已经用 Sql Server、Access、Sqlite 和 Oracle 进行了测试(但请注意,this will not work as is with ODP .NET...)。

【讨论】:

  • 谢谢,麦克。这似乎回答了我的问题。我还将深入研究 Salamanca 代码以获取其他一些提示。干杯。
【解决方案2】:

似乎对此没有约定或 API。诸如 nhibernate 之类的 ORM 也为每个驱动程序实现了自己的占位符前缀映射。

【讨论】:

  • 实际上这就是我获得当前实施方法的地方。我将发布更多代码,以便您了解我现在在做什么。
【解决方案3】:

您可能会受到轻微的性能影响并使用System.Data.OleDb 类。这样,无论数据库如何,您都可以使用相同的代码。或者你可以使用像Unity 这样的控制反转框架。然后,您可以要求为您的数据访问类注入适当的数据库工厂、参数以及调用者希望使用的工厂。

【讨论】:

  • 这是一个选项。但是,对于我认为是实用程序类的东西,我宁愿避免使用 IoC 框架——这对我的初级开发人员来说太过分了,更不用说我的客户了(咨询限制了我的选择)。
猜你喜欢
  • 2015-12-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-09-20
  • 1970-01-01
  • 2019-03-28
  • 2011-03-09
相关资源
最近更新 更多