2014-04-08 7 views
0

Я работаю над функцией SQLFormat, которая работает как обычный String.Format.Текущий индекс в пользовательском String.Format

А теперь я застрял на том, зависит от какой индекс тока Format в.

К примеру, у нас есть этот SQL-запрос (псевдопользователей, а не фактическое использование).

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 в этом случае.

ответ

1

Глядя на вашем примере и работает он здесь был интересный урок о материале, который я не видел раньше, так что я могу быть полностью выключен ..

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); 
     } 
+0

Удивительный, с этим мне есть с чем работать. Я просто должен его поменять, поэтому я могу обрабатывать и неименованные жетоны. Большое спасибо! – NoLifeKing

+0

Хорошо; было несколько вещей, чтобы рассмотреть, например. в другом ответе тоже. – TaW

+0

Да, я бросаю все в SqlParameters, чтобы быть уверенным, что я избавлюсь от рисков, связанных с инъекциями, и так далее. :) – NoLifeKing

1

Во-первых, вы не должны использовать string.IsNullOrEmpty, замените его string.IsNullOrWhiteSpace, как строка, содержащая пробелы не будут рассматриваться как пустые.

Во-вторых, вы не должны использовать параметры так, как вы это делаете, это проблема безопасности, для которой может возникнуть SQL-инъекция. Вместо этого добавьте объект параметров в свою команду.

В-третьих, используя объекты параметров, вы сможете повторно использовать один и тот же параметр дважды, если вы используете именованные параметры.

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. 
} 

ОТКАЗ

образец кода не компилируется из верхней части моей головы. Предоставляется как-будто, например, только цель.

+0

Модифицировал мой код, чтобы вместо этого использовать 'string.IsNullOrWhiteSpace'. ^^ – NoLifeKing

0

Это стандартное поведение с библиотеками SQL в целом. Библиотека не знает, что вы всегда будете использовать одно и то же значение для 1-го и 3-го параметров.

+2

Этот ответ as-is - отличное введение в ответ, который фактически решит проблему. Пожалуйста обновите. – AndyG