2010-10-15 41 views
3

我有一个带参数的SqlCommand。由于某些外部要求(见PS),我需要一个T-SQL字符串代替,即我需要... @parameter ...... [email protected] ...取代(例如:O'Brien - >'O''Brien'02 Mar 2010 - >'20100302'3.5 - >3.5)。将SqlCommand转换为T-SQL命令

我知道我可以很容易地在家酿造这样的解决方案,但正确的转义是非常棘手的(确保您获得正确的日期和数字格式,注意字符串中的引号等),我想我不是唯一一个需要这个,所以我的问题是:

是否有一个现有的解决方案,用参数将SqlCommand转换为嵌入参数并正确转义的单个T-SQL语句?

PS:我们需要这样做,因为我们需要使用SqlCommand作为MS Access报表的数据源......并且Access中的“pass-though querydefs”不支持参数化SQL。

PPS:我知道有similarquestions,但他们都认为SQL服务器莫名其妙地做这种转换(@parameter - >一些逃脱代表字符串值),所以答案(“SQL Server不这样做“)在这里不适用。我知道SQL Server中不存在这种转换。

+0

我不想被否定,但正如其他问题已经澄清 - 并且正如您自己提到的那样 - 您要查找的TSQL字符串永远不会生成。所以我不确定你想要得到什么答案,除了自己推出或修改你的应用程序? – Pondlife 2010-10-15 14:55:02

+0

@Pondlife:我希望得到这样的答案:“是的,我之前遇到过这个问题,并且我已经编写并发布了一个C#/ VB {链接到源代码}的例程,它根据在{link to MSDN}找到SQL Server转义规范。“ – Heinzi 2010-10-15 15:00:22

回答

5

我认为这是非常清晰地写出来与参数的SQL查询,并有主SELECT查询前几DECLARE @p1 type;SET @p1 = 'value';线和留在SqlCommand实例不变原来的查询文本。我有一些示例代码,如果你想看到这一点,可以这样做。

如果您确实坚持在查询文本中简单地进行参数值替换,那么找到您正在与之通话的特定于服务器的T-SQL语法,解析查询,直到找到参数引用,并根据T-SQL语法的字符串转义规则将其替换为文本。

编辑

因为我这样一个富于幻想的人,我会得到你开始用自己的方式与适当的转义输出格式的SQL参数。这是不完整的,因为它不处理man和SQL Server已知的每种数据库类型,但它为我完成了工作,因为它处理了我最常用的类型。正如我在我的评论中所说的,只需将单引号字符作为连续的两个单引号字符转义似乎足以进行正确的字符串转义。您必须对各种SQL Server版本使用的T-SQL语法进行双倍和三倍的检查,以确保这是足够好的。

private static string FormatSqlValue(System.Data.Common.DbParameter prm) 
{ 
    if (prm.Value == DBNull.Value) return "NULL"; 
    switch (prm.DbType) 
    { 
     case System.Data.DbType.Int32: return (prm.Value.ToString()); 
     case System.Data.DbType.String: return String.Format("'{0}'", ScrubSqlString((string)prm.Value)); 
     case System.Data.DbType.AnsiString: return String.Format("'{0}'", ScrubSqlString((string)prm.Value)); 
     case System.Data.DbType.Boolean: return ((bool)prm.Value ? "1" : "0"); 
     case System.Data.DbType.DateTime: return String.Format("'{0}'", prm.Value.ToString()); 
     case System.Data.DbType.DateTime2: return String.Format("'{0}'", prm.Value.ToString()); 
     case System.Data.DbType.Decimal: return (prm.Value.ToString()); 
     case System.Data.DbType.Guid: return String.Format("'{0}'", prm.Value.ToString()); 
     case System.Data.DbType.Double: return (prm.Value.ToString()); 
     case System.Data.DbType.Byte: return (prm.Value.ToString()); 
     // TODO: more conversions. 
     default: return (prm.DbType.ToString()); 
    } 
} 

private static string FormatSqlValue(Type type, object value) 
{ 
    if (value == null) 
     return "NULL"; 
    // Handle Nullable<T> types: 
    if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>)) 
    { 
     // If the Nullabe<T> value has been found to have !HasValue, return "NULL": 
     if (!(bool)type.GetProperty("HasValue").GetValue(value, null)) 
      return "NULL"; 
     // Try our best to format the underlying non-nullable value now: 
     return FormatSqlValue(type.GetGenericArguments()[0], type.GetProperty("Value").GetValue(value, null)); 
    } 
    if (type == typeof(Int32)) return value.ToString(); 
    if (type == typeof(String)) return String.Format("'{0}'", ScrubSqlString((string)value)); 
    if (type == typeof(Boolean)) return ((bool)value ? "1" : "0"); 
    if (type == typeof(DateTime)) return String.Format("'{0}'", value.ToString()); 
    if (type == typeof(Decimal)) return (value.ToString()); 
    if (type == typeof(Guid)) return String.Format("'{0}'", value.ToString()); 
    if (type == typeof(Double)) return (value.ToString()); 
    if (type == typeof(Byte)) return (value.ToString()); 
    // TODO: complete the mapping... 
    return value.ToString(); 
} 

private static string ScrubSqlString(string value) 
{ 
    StringBuilder sb = new StringBuilder(); 
    int i = 0; 
    while (i < value.Length) 
    { 
     if (value[i] == '\'') 
     { 
      sb.Append("\'\'"); 
      ++i; 
     } 
     else 
     { 
      sb.Append(value[i]); 
      ++i; 
     } 
    } 
    return sb.ToString(); 
} 

private static string FormatSqlParameter(System.Data.Common.DbParameter prm) 
{ 
    StringBuilder sbDecl = new StringBuilder(); 
    sbDecl.Append(prm.ParameterName); 
    sbDecl.Append(' '); 
    switch (prm.DbType) 
    { 
     case System.Data.DbType.Int32: sbDecl.Append("int"); break; 
     // SQL does not like defining nvarchar(0). 
     case System.Data.DbType.String: sbDecl.AppendFormat("nvarchar({0})", prm.Size == -1 ? "max" : prm.Size == 0 ? "1" : prm.Size.ToString()); break; 
     // SQL does not like defining varchar(0). 
     case System.Data.DbType.AnsiString: sbDecl.AppendFormat("varchar({0})", prm.Size == -1 ? "max" : prm.Size == 0 ? "1" : prm.Size.ToString()); break; 
     case System.Data.DbType.Boolean: sbDecl.Append("bit"); break; 
     case System.Data.DbType.DateTime: sbDecl.Append("datetime"); break; 
     case System.Data.DbType.DateTime2: sbDecl.Append("datetime2"); break; 
     case System.Data.DbType.Decimal: sbDecl.Append("decimal"); break; // FIXME: no precision info in DbParameter! 
     case System.Data.DbType.Guid: sbDecl.Append("uniqueidentifier"); break; 
     case System.Data.DbType.Double: sbDecl.Append("double"); break; 
     case System.Data.DbType.Byte: sbDecl.Append("tinyint"); break; 
     // TODO: more conversions. 
     default: sbDecl.Append(prm.DbType.ToString()); break; 
    } 
    return sbDecl.ToString(); 
} 
+0

+1,谢谢,这是一个不错的方法。但是,原来的问题仍然存在,因为SET @ p1 ='value''需要与'WHERE myfield ='value''相同的转义类型。所以“*根据语法规则逃避”在*两种情况下都是必需的。你有链接到SQL Server的“转义规则”吗? (SO上的这类“逃生规则”的问题立即用“仅使用参数化查询”来回答,这在这种情况下不是非常有用......) – Heinzi 2010-10-15 14:57:52

+0

我意识到在发布后:)对不起。我只做了基本的字符串转义规则,这些规则对于我的显示目的来说“足够好”。如果你只是掩饰转义字符的转义,我认为你已经设定好了。 – 2010-10-15 16:17:58

+0

@Heinzi:我用我为我的SQL审计工具开发的代码更新了答案,该工具向人类用户显示SQL代码,但不打算用T-SQL引擎进行分析。如果可能的话,你会发现TODO和FIXME指出可以改进的地方。希望有所帮助! – 2010-10-15 16:33:24