Именование параметров с использованием System.Data.Common

2

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

Вопрос: Это все лишнее и есть какая-то простая вещь, которую я пропускаю, которая должна просто позаботиться об этом? Если мой IDbCommand.CommandText = "выберите id, имя из 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();
    }

Параметр ОпределитьProviderSpecificParameters в основном определяет "?" или ":" или "@" или что-то еще.

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

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

    private void ОпределитьProviderSpecificParameters()   {       // Проверяем наличие поддерживаемых поставщиков. Это значит, что параметризованные запросы ограничивают       // по пространственной протяженности правильно создаются.       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;
        }
    }
    
  • вызовите немного помощника, прежде чем выполнять каждую команду для "очистки" или "параметрического", или, тем не менее, мы называем этот полузасужденный взлом:

    private void MakeProviderSpecific(IDbCommand command)
    {
        foreach (IDataParameter param in command.Parameters)
        {
            param.ParameterName = GetProviderSpecificCommandText(param.ParameterName);
        }
        command.CommandText = GetProviderSpecificCommandText(command.CommandText);
    }
    
  • И это вызывает небольшое регулярное выражение:

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

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

  • 0
    Не уверен, но одним из способов сделать то, что вы смотрите, является NHibernate.
  • 0
    Я использую NHibernate для других проектов, но это библиотека утилит для моих других разработчиков, которая дает им быстрый, но не слишком грязный подход к доступу к БД. Кроме того, я использую это как ядро моего кода для клиентов с открытым исходным кодом. Да, я проверяю код доступа к данным больше раз, чем могу думать! :-(
Теги:
database

3 ответа

1
Лучший ответ

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

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

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

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

    • Либо через 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...).

  • 0
    Спасибо, Мак. Кажется, это отвечает на мой вопрос. Я более подробно рассмотрю код Саламанки для некоторых других советов. Приветствия.
1

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

  • 0
    На самом деле, именно отсюда я получил свой текущий подход к реализации. Я выложу немного больше кода, чтобы вы могли видеть, что я делаю сейчас.
0

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

  • 0
    Это вариант. Однако я бы предпочел избегать инфраструктуры IoC для чего-то, что я считаю служебным классом - слишком сложным для моих младших разработчиков, не говоря уже о моих клиентах (консультации немного ограничивают мои возможности).

Ещё вопросы

Сообщество Overcoder
Наверх
Меню