This is probably an oldie-but-goodie. I am using System.Data.Common for an interchangeable Oracle/SQL Server/SQLite data access library. During the constructor I take the connection string name and use that to determine the underlying provider type. The reason I do this is to handle the different IDbParameter naming conventions for each provider. For example, Oracle likes :parameter whereas SQL Server and SQLite like @parameter. The default is ? to cover Oledb.
Question: Is this all unnecessary and is there some simple thing I'm missing that should simply take care of this? If my IDbCommand.CommandText = "select id, name from my.table where id = :id" am I covered? For now I'm just adopting ? as the default and then RegEx'ing my way to the right parameter identifier before executing the command.
Thanks.
/// <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();
}
The DetermineProviderSpecificParameters bit basically figures out "?" or ":" or "@" or something else.
UPDATE Here's how I'm handling the details thus far:
Get the right parameter string:
private void DetermineProviderSpecificParameters() { // Check for supported providers. This is so that parametized queries to limit // by spatial extent are correctly created. 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; } }
call a little helper before I execute each command to "cleanify" or "parameterific" it or however we call this half-assed hack:
private void MakeProviderSpecific(IDbCommand command) { foreach (IDataParameter param in command.Parameters) { param.ParameterName = GetProviderSpecificCommandText(param.ParameterName); } command.CommandText = GetProviderSpecificCommandText(command.CommandText); }
And this calls a little regex to do to do:
public string GetProviderSpecificCommandText(string rawCommandText) { return Regex.Replace(rawCommandText, @"\B\?\w+", new MatchEvaluator(SpecificParam)); }
Yuck. Still searching for a relatively simple solution but the advice thus far is certainly appreciated.