Именование параметров с использованием System.Data.Common - PullRequest
2 голосов
/ 10 августа 2009

Это, наверное, старенький, но гуди. Я использую System.Data.Common для сменной библиотеки доступа к данным Oracle / SQL Server / SQLite. Во время конструктора я беру имя строки подключения и использую его для определения базового типа провайдера. Причина, по которой я это делаю, состоит в том, чтобы обрабатывать разные соглашения об именах IDbParameter для каждого провайдера. Например, Oracle любит: параметр, тогда как SQL Server и SQLite любят @parameter. По умолчанию это? накрыть оледб.

Вопрос: Это все излишне и есть какая-то простая вещь, по которой я скучаю, которая должна просто позаботиться об этом? Если мой IDbCommand.CommandText = "выбрать идентификатор, имя из my.table где id =: id", я покрыт? Пока я просто усыновляю? как значение по умолчанию и затем RegEx'ing мой путь к правильному идентификатору параметра перед выполнением команды.

Спасибо.

        /// <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 в основном вычисляет "?" или ":" или "@" или что-то еще.

UPDATE Вот как я до сих пор работаю с деталями:

  1. Получить правильную строку параметров:

    private void DetermineProviderSpecificParameters () { // Проверка поддерживаемых провайдеров. Это чтобы параметризованные запросы ограничивались // по пространственному экстенту правильно созданы. string shortName = _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));
    }
    

Тьфу. Все еще в поисках относительно простого решения, но совет, безусловно, ценится.

Ответы [ 3 ]

1 голос
/ 11 августа 2009

Я сделал что-то подобное для Саламанка : см. ParameterBuilder.cs . Этот код использует:

  • защищенные методы для DbCommandBuilder, вызываемые с помощью отражения (будет работать только в режиме полного доверия, я полагаю): GetParameterPlaceholder и GetParameterName.
  • информация, возвращаемая DbConnection.GetSchema. В идеальном мире я мог использовать только это, но я так и не понял, как далеко ...

Дело в том, что вам нужно правильное имя для вашего параметра ("@name" в Sql Server, "name" в Oracle) и действительный заполнитель в вашем SQL-запросе ("@name" в Sql Server, ":name" в Oracle).

  1. При правильном подключении GetParameterName даст вам правильное имя для вашего параметра.
  2. Создайте свой заполнитель:

    • Либо через GetParameterPlaceholder.
    • Или запросите значение DbMetaDataColumnNames.ParameterMarkerFormat, содержащееся в схеме вашего соединения . Вы должны быть в состоянии создать свой заполнитель, используя эту строку в качестве строки формата, принимая в качестве входных данных предыдущее имя параметра (подразумевая, что строка формата "{0}" для Sql Server и ":{0}" для Oracle):

      // 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 (но обратите внимание, что, что неудивительно, это не будет работать, как с ODP .NET ...).

1 голос
/ 11 августа 2009

Кажется, для этого нет соглашения или API. ORM, такие как nhibernate, также реализует свое собственное отображение префикса заполнителя для каждого драйвера.

0 голосов
/ 11 августа 2009

Вы можете слегка снизить производительность и использовать классы System.Data.OleDb . Таким образом, вы можете использовать один и тот же код независимо от базы данных. Или вы можете использовать Inversion of Control Framework, например Unity . Затем вы можете потребовать, чтобы в ваш класс доступа к данным была введена соответствующая фабрика для базы данных, параметров и так, чтобы вызывающая сторона захотела использовать.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...