Текущий индекс в пользовательском 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-го параметров.

Другие вопросы по тегам