简体   繁体   English

自定义String.Format中的当前索引

[英]Current index in custom String.Format

I'm working on a SQLFormat-function, that work like the normal String.Format . 我正在研究一个SQLFormat函数,它的工作方式与普通的String.Format类似。

And now I'm stuck on being reliant on which index the current Format is in. 而现在我仍然依赖于当前Format所在的索引。

For example, we have this SQL-query (Psuedo, not actual usage). 例如,我们有这个SQL查询(Psuedo,而不是实际用法)。

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

This is being formatted like this right now: 现在这样格式化:

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

Where it outputs @p2 , I want it to reuse @p0 (since I've already added that parameter) 在它输出@p2 ,我希望它重用@p0 (因为我已经添加了该参数)

Here's my class that does the formatting. 这是我的类,它进行格式化。 Currently I'm using an static int for the indexing 目前我正在使用静态int进行索引

I haven't found out if I can get the current parameter-index). 我还没有发现我是否可以获得当前的参数索引。

One way would be to skip the formatting, but I need this, so I can skip replacing the tokens myself. 一种方法是跳过格式化,但我需要这个,所以我可以自己跳过替换标记。 (I think that String.Format is faster than code I could produce :P) (我认为String.Format比我可以生成的代码更快:P)

UPDATED : I have updated the code with my current code, I prepare all the tokens on beforehand and push in the parameter-names as values instead of the values. 更新 :我已使用当前代码更新代码,我事先准备好所有令牌并将参数名称作为值而不是值推送。

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

Example usage: 用法示例:

var sql = @"SELECT {0:SQL;userID}, {0:SQL;userID}";

var retSql = String.Format(new SqlFormat(), sql, new[] { 650 });
Console.WriteLine(retSql);

This returns SELECT @p0 /* userID */, @p1 /* userID */ 这将返回SELECT @p0 /* userID */, @p1 /* userID */

instead of 代替

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

Is there some way that I can get the current index? 有什么方法可以获得当前的指数吗? 0 in this case. 在这种情况下为0

Looking at your example and running it here was an interesting lesson about stuff I hadn't seen before, so I may be completely off.. 看看你的例子并在这里运行它是一个关于我以前没有见过的东西的有趣课程,所以我可能完全不在...

the 0 in the {0: looks to my like the index of the placeholder to insert. 0{0:看起来像我的占位符的索引中插入。 Your format always has only one value, so it will always use and need only a 0 . 您的格式始终只有一个值,因此它始终使用且只需要0

The internal (not static!) int is obviously nonsense. 内部(非静态!)int显然是无意义的。 And the idea that you want to get at the current index seems wrong, too. 你想要获得当前指数的想法似乎也是错误的。

To me the solution would be that you either actually do provide two values, the number and the name not just the name. 对我来说,解决方案是你要么实际提供两个值,数字和名称而不仅仅是名称。 Can't help you with the syntax. 无法帮助您解决语法问题。 Or, and that would be better I think, that you deduct the number from the name . 或者,我认为, 从名称中扣除数字会更好。

You could build a dictionary and pull the number from it, whenever you meet a name that is already in the dictionary. 只要您遇到字典中已有的名称,就可以构建一个字典并从中提取数字。 The other names get added with an incremented number. 其他名称将添加一个递增的数字。

Here is a quick one; 这是一个快速的; seems to work..: 似乎工作..:

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

First, you shan't use string.IsNullOrEmpty , replace it with string.IsNullOrWhiteSpace , as a string containing spaces will not be considered as empty. 首先,您不应使用string.IsNullOrEmpty ,将其替换为string.IsNullOrWhiteSpace ,因为包含空格的字符串不会被视为空。

Second, you shan't use parameters the way you do, it is a security concern for which SQL injection might occur. 其次,你不应该像你那样使用参数,这是一个安全问题,可能会发生SQL注入。 Add parameters object to your command instead. 请将参数对象添加到命令中。

Third, using parameter objects, you will be able to reuse same parameter twice, as long as you make use of named-parameters. 第三,使用参数对象,只要使用命名参数,就可以重复使用相同的参数两次。

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

DISCLAIMER 免责声明

Code sample not compiled out of the top of my head. 代码示例没有编译出我的头脑。 Provided as-is for example purpose only. 仅作为示例提供。

This is standard behavior with SQL libraries in general. 这是SQL库的标准行为。 The library doesn't know that you'll always use the same value for the 1st and 3rd parameters. 该库不知道您将始终对第一个和第三个参数使用相同的值。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM