Это, вероятно, старик-но-добрый. Я использую 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));
}
Тьфу. Все еще ищут относительно простое решение, но совет, безусловно, оценен.
Я сделал что-то подобное для 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...).
Кажется, для этого нет соглашения или API. ORM, такие как nhibernate, также реализуют собственное сопоставление префикса-заполнителя для каждого драйвера.
Вы можете сделать небольшой удар производительности и использовать классы System.Data.OleDb. Таким образом, вы можете использовать один и тот же код независимо от базы данных. Или вы можете использовать инфраструктуру инверсии управления, например Unity. Затем вам может потребоваться, чтобы ваш класс доступа к данным был введен соответствующим factory для базы данных, параметрами и тем, что вызывающий пользователь хочет использовать.