2014-04-08 2 views
0

보통 String.Format처럼 작동하는 SQLFormat 함수에서 작업하고 있습니다.커스텀 String.Format의 현재 인덱스

그리고 지금 나는 현재 Format가에있는 인덱스에 의존되는에 붙어있어.

예를 들어, 우리는이 SQL 쿼리 (사이비가 아닌 실제 사용)가 있습니다.

SELECT * 
FROM users 
WHERE userID = {0:SQL;userID} 
AND 10 != {1:SQL;strangeID} 
AND 100 = {0:SQL;userID} 

지금이 바로 같은 형식되고있다 : 그것은 @p2을 출력하는 경우 (이미 해당 매개 변수를 추가 했으므로)

SELECT * 
FROM users 
WHERE userID = @p0 /* userID */ 
AND 10 != @p1 /* strangeID */ 
AND 100 = @p2 /* userID */ 

, 나는 그것이 @p0을 재사용 할 여기

내 형식을 지정하는 클래스 현재 색인 생성에 정적 int를 사용하고 있습니다.

현재 매개 변수 색인을 얻을 수 있는지 알 수 없습니다.)

한 가지 방법은 서식을 건너 뛰는 것이지만 필요하므로 토큰을 직접 건너 뛸 수 있습니다.

업데이트 : (P 나는 String.Format 내가 생산할 수있는 코드보다 빠르다는 것을 생각) : 내 현재 코드로 코드를 업데이트 한, 나는 사전에 모든 토큰을 준비하고 대신 값으로 파라미터 이름에 밀어 값의.

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 */, @p0 /* userID */

SELECT @p0 /* userID */, @p1 /* userID */

반환? 이 경우 0.

답변

1

{0:에 ..

0 당신의 예를 보면 그것이 여기 물건 내가 전에 보지 않았다면에 대한 흥미로운 교훈 실행, 그래서 나는 완전히 해제 할 수는 인덱스처럼 내 보이는 삽입 할 자리 표시 자입니다. 형식에는 항상 하나의 값만 있으므로 항상 사용되며 0 만 필요합니다.

int (정적이 아님!) 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

굉장하고, 이것과 함께할만한 것이 있습니다. mod를 사용해야 만하므로 이름이 지정되지 않은 토큰도 처리 할 수 ​​있습니다. 고마워요! – NoLifeKing

+0

좋습니다. 예를 들어 고려해야 할 몇 가지 사항이있었습니다. securitywise 다른 대답도. – TaW

+0

그래, 나는 주사 위험을 없애는 등 SqlParameters에 모든 것을 던진다. :) – NoLifeKing

1

첫째, 당신은 비어있는 것으로 간주되지 않습니다 공백이 포함 된 문자열로, string.IsNullOrWhiteSpace로 교체, string.IsNullOrEmpty를 사용하지 않아야한다.

둘째, 매개 변수를 사용하지 말아야합니다. 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 라이브러리의 일반적인 작동입니다. 라이브러리는 첫 번째와 세 번째 매개 변수에 항상 같은 값을 사용한다는 것을 모릅니다.

+2

이 답변은 실제적으로 문제를 해결할 수있는 답을 제시하는 훌륭한 소개입니다. 업데이트하십시오. – AndyG

관련 문제