Текущий индекс в пользовательском String.Format
Я работаю над SQLFormat-функцией, которая работает как обычно String.Format
,
И теперь я застрял на том, что зависит от индекса текущего Format
в.
Например, у нас есть этот SQL-запрос (Psuedo, а не фактическое использование).
SELECT *
FROM users
WHERE userID = {0:SQL;userID}
AND 10 != {1:SQL;strangeID}
AND 100 = {0:SQL;userID}
Это форматируется прямо сейчас:
SELECT *
FROM users
WHERE userID = @p0 /* userID */
AND 10 != @p1 /* strangeID */
AND 100 = @p2 /* userID */
Где это выводит @p2
Я хочу это использовать повторно @p0
(так как я уже добавил этот параметр)
Вот мой класс, который делает форматирование. В настоящее время я использую статический int для индексации
Я не выяснил, смогу ли я получить текущий индекс параметра).
Одним из способов было бы пропустить форматирование, но мне это нужно, чтобы я мог пропустить замену токенов самостоятельно. (Я думаю что String.Format
быстрее, чем код, который я мог бы произвести:P)
ОБНОВЛЕНО: я обновил код моим текущим кодом, заранее подготовил все токены и вставил имена параметров как значения вместо значений.
public class SqlFormat : IFormatProvider, ICustomFormatter
{
public object GetFormat(Type formatType)
{
if (formatType == typeof(ICustomFormatter))
return this;
return null;
}
internal int IndexCounter = 0;
Dictionary<string, int> dict = new Dictionary<string, int>();
public string Format(string format, object arg, IFormatProvider formatProvider)
{
if (!this.Equals(formatProvider))
return null;
if (string.IsNullOrWhiteSpace(format))
format = "SQL;field";
var formats = format.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries);
format = formats[0];
if (format == "SQL")
{
string ind = IndexCounter.ToString();
if (dict.ContainsKey(formats[1])) ind = dict[formats[1]].ToString();
else dict.Add(formats[1], IndexCounter++);
var pName = arg;
return pName + (formats.Length > 1 ? " /* " + formats[1] + " */" : "");
}
else
{
return HandleOtherFormats(format, arg);
}
}
public string HandleOtherFormats(string format, object arg)
{
return string.Format(format, arg);
}
}
Пример использования:
var sql = @"SELECT {0:SQL;userID}, {0:SQL;userID}";
var retSql = String.Format(new SqlFormat(), sql, new[] { 650 });
Console.WriteLine(retSql);
Это возвращает SELECT @p0 /* userID */, @p1 /* userID */
вместо
SELECT @p0 /* userID */, @p0 /* userID */
Есть ли способ получить текущий индекс? 0
в этом случае.
3 ответа
Рассмотрение вашего примера и его запуск здесь был интересным уроком о вещах, которых я раньше не видел, так что я могу быть совершенно не в себе...
0
в {0:
выглядит как индекс заполнителя для вставки. Ваш формат всегда имеет только одно значение, поэтому он всегда будет использовать и нужен только 0
,
Внутренний (не статичный!) Int, очевидно, чепуха. И идея, которую вы хотите получить по текущему индексу, тоже кажется неверной.
Для меня решение состоит в том, что вы либо фактически предоставляете два значения, число и имя, а не просто имя. Не могу помочь вам с синтаксисом. Или, и это было бы лучше, я думаю, что вы вычитаете число из имени.
Вы можете создать словарь и извлечь номер из него всякий раз, когда вы встречаете имя, которое уже есть в словаре. Другие имена добавляются с увеличенным числом.
Вот быстрый; похоже на работу..
public class SqlFormat : IFormatProvider, ICustomFormatter
{
public object GetFormat(Type formatType)
{
if (formatType == typeof(ICustomFormatter))
return this;
return null;
}
internal int IndexCounter = 0;
Dictionary<string, int> dict = new Dictionary<string, int>();
public string Format(string format, object arg, IFormatProvider formatProvider)
{
if (!this.Equals(formatProvider))
return null;
if (string.IsNullOrEmpty(format))
format = "SQL";
var formats = format.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries);
format = formats[0];
if (format == "SQL")
{
string ind = IndexCounter.ToString();
if (dict.ContainsKey(formats[1])) ind = dict[formats[1]].ToString();
else dict.Add(formats[1], IndexCounter++);
var pName = "@p" + ind;
return pName + (formats.Length > 1 ? " /* " + formats[1] + " */" : "");
}
else
{
return HandleOtherFormats(format, arg);
}
}
public string HandleOtherFormats(string format, object arg)
{
return string.Format(format, arg);
}
Во-первых, вы не будете использовать string.IsNullOrEmpty
замените его string.IsNullOrWhiteSpace
, так как строка, содержащая пробелы, не будет считаться пустой.
Во-вторых, вы не будете использовать параметры так, как вы это делаете, это проблема безопасности, для которой может произойти внедрение SQL. Вместо этого добавьте объект параметров в вашу команду.
В-третьих, используя объекты параметров, вы сможете повторно использовать один и тот же параметр дважды, если вы используете named-параметры.
using (var cnx = new SqlConnection(connectionString)) {
cnx.Open();
var cmd = cnx.CreateCommand();
cmd.CommandText = sql; // sql is actually your string containing named-parameters
var param1 = cmd.CreateParameter();
param1.DbType = DbType.Int32;
param1.ParameterDirection = ParameterDirection.Input;
param1.Name = "@p0";
param1.Value = value;
cmd.ExecuteQuery(); // Make sure to use proper method call here.
}
ОТКАЗ ОТ ОТВЕТСТВЕННОСТИ
Пример кода не скомпилирован из головы. Предоставляется только для примера.
Это стандартное поведение с библиотеками SQL в целом. Библиотека не знает, что вы всегда будете использовать одно и то же значение для 1-го и 3-го параметров.