我正在研究一个类似于普通String.Format
的SQLFormat函数。
现在我不得不依赖当前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
。
答案 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);
}
答案 1 :(得分: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.
}
<强>声明强>
代码示例没有编译出我的头脑。仅作为示例提供。
答案 2 :(得分:0)
这是SQL库的标准行为。图书馆不知道您将始终对第1和第3个参数使用相同的值。