Word Merge的字符串替换

时间:2011-03-31 15:54:43

标签: c# asp.net sql ms-word

使用asp.net 4

我们在工作中做了很多Word合并。而不是使用Word的复杂条件语句我想嵌入我自己的语法。类似的东西:

Dear Mr. { select lastname from users where userid = 7 },
Your invoice for this quarter is: ${ select amount from invoices where userid = 7 }.
......

理想情况下,我希望将其变为:

string.Format("Dear Mr. {0},  Your invoice for this quarter is: ${1}", sqlEval[0], sqlEval[1]);

任何想法?

4 个答案:

答案 0 :(得分:0)

嗯,我真的不建议为此推出自己的解决方案,但我会按照要求回答问题。

首先,您需要处理文本并提取SQL语句。为此你需要一个简单的解析器:

/// <summary>Parses the input string and extracts a unique list of all placeholders.</summary>
/// <remarks>
///  This method does not handle escaping of delimiters
/// </remarks>
public static IList<string> Parse(string input)
{
    const char placeholderDelimStart = '{';
    const char placeholderDelimEnd = '}';

    var characters = input.ToCharArray();
    var placeHolders = new List<string>();
    string currentPlaceHolder = string.Empty;
    bool inPlaceHolder = false;
    for (int i = 0; i < characters.Length; i++)
    {
        var currentChar = characters[i];

        // Start of a placeholder
        if (!inPlaceHolder && currentChar == placeholderDelimStart)
        {
            currentPlaceHolder = string.Empty;
            inPlaceHolder = true;
            continue;
        }

        // Start of a placeholder when we already have one
        if (inPlaceHolder && currentChar == placeholderDelimStart)
            throw new InvalidOperationException("Unexpected character detected at position " + i);

        // We found the end marker while in a placeholder - we're done with this placeholder
        if (inPlaceHolder && currentChar == placeholderDelimEnd)
        {
            if (!placeHolders.Contains(currentPlaceHolder))
                placeHolders.Add(currentPlaceHolder);
            inPlaceHolder = false;
            continue;
        }

        // End of a placeholder with no matching start
        if (!inPlaceHolder && currentChar == placeholderDelimEnd)
            throw new InvalidOperationException("Unexpected character detected at position " + i);

        if (inPlaceHolder)
            currentPlaceHolder += currentChar;
    }
    return placeHolders;
}

好的,这样就可以获得从输入文本中提取的SQL语句列表。您可能希望调整它以使用正确类型的解析器异常和一些输入保护(为了清楚起见,我省略了)。

现在您只需要将这些占位符替换为已评估的SQL的结果:

// Sample input
var input = "Hello Mr. {select firstname from users where userid=7}";

string output = input;
var extractedStatements = Parse(input);
foreach (var statement in extractedStatements)
{
    // Execute the SQL statement
    var result = Evaluate(statement);

    // Update the output with the result of the SQL statement
    output = output.Replace("{" + statement + "}", result);
}

这显然不是最有效的方法,但我认为它足以证明这个概念而不会使水变得混乱。

您需要定义Evaluate(string)方法。这将处理执行SQL。

答案 1 :(得分:0)

我刚刚在这里为一家律师事务所完成了这样的专有解决方案。 我评估了一款名为Windward的产品报告。这有点贵,特别是如果你需要大量的副本,但是对于一个用户来说它并不坏。

它可以从XML或SQL数据源中提取(如果我记得的话,还可以更多)。

可能值得一看(不,我不为他们工作,只评估他们的东西)

答案 2 :(得分:0)

您可能想要查看codeplex上的剃须刀引擎项目

http://razorengine.codeplex.com/

在模板中使用SQL等看起来不错。我建议你为每个模板制作一个ViewModel。

Razor的东西真的很容易使用。只需添加一个引用,导入命名空间,然后像这样调用Parse方法:

(VB家伙原谅语法!)

MyViewModel myModel = new MyViewModel("Bob",150.00); //set properties

string myTemplate = "Dear Mr. @Model.FirstName,  Your invoice for this quarter is: @Model.InvoiceAmount";

string myOutput = Razor.Parse(myTemplate, myModel);

你的字符串可以来自任何地方 - 我使用存储在数据库中的模板,你可以从文件或其他任何地方加载它。它作为视图引擎非常强大,你可以做有条件的东西,循环等等。

答案 3 :(得分:0)

我最终推出了自己的解决方案,但谢谢。我真的不喜欢陈述。我需要重构它们。这是:

var mailingMergeString = new MailingMergeString(input);
var output = mailingMergeString.ParseMailingMergeString();

public class MailingMergeString
{
    private string _input;

    public MailingMergeString(string input)
    {
        _input = input;
    }

    public string ParseMailingMergeString()
    {
        IList<SqlReplaceCommand> sqlCommands = new List<SqlReplaceCommand>();
        var i = 0;
        const string openBrace = "{";
        const string closeBrace = "}";

        while (string.IsNullOrWhiteSpace(_input) == false)
        {
            var sqlReplaceCommand = new SqlReplaceCommand();
            var open = _input.IndexOf(openBrace) + 1;
            var close = _input.IndexOf(closeBrace);
            var length = close != -1 ? close - open : _input.Length;
            var newInput = _input.Substring(close + 1);
            var nextClose = newInput.Contains(openBrace) ? newInput.IndexOf(openBrace) : newInput.Length;

            if (i == 0 && open > 0)
            {
                sqlReplaceCommand.Text = _input.Substring(0, open - 1);
                _input = _input.Substring(open - 1);
            }
            else
            {
                sqlReplaceCommand.Command = _input.Substring(open, length);
                sqlReplaceCommand.PlaceHolder = openBrace + i + closeBrace;
                sqlReplaceCommand.Text = _input.Substring(close + 1, nextClose);
                sqlReplaceCommand.NewInput = _input.Substring(close + 1);

                _input = newInput.Contains(openBrace) ? sqlReplaceCommand.NewInput : string.Empty;
            }

            sqlCommands.Add(sqlReplaceCommand);
            i++;
        }

        return sqlCommands.GetParsedString();
    }

    internal class SqlReplaceCommand
    {
        public string Command { get; set; }

        public string SqlResult { get; set; }

        public string PlaceHolder { get; set; }

        public string Text { get; set; }

        protected internal string NewInput { get; set; }
    }
}

internal static class SqlReplaceExtensions
{
    public static string GetParsedString(this IEnumerable<MailingMergeString.SqlReplaceCommand> sqlCommands)
    {
        return sqlCommands.Aggregate("", (current, replaceCommand) => current + (replaceCommand.PlaceHolder + replaceCommand.Text));
    }
}