1

I used have a template string, read from DB and or text file:

var template="Your order {0} has dispatched from {1}, ETA {2:HH:MM dd.MM.yyyy}";

And then used string.Format(template, orderNo, warehouse, eta) to inject correct values.

Now, a business decision was made, to change those templates to use more meaningful markers:

var template="Your order {orderNo} has dispatched from {warehouse}, ETA {eta:HH:MM dd.MM.yyyy}";

And now I'm not sure what would be the best way to inject values, taking into consideration that, for example, eta field has formatting that is "hardcoded" into template (so that we could use different DateTime format depending on language)

One idea was to load the template into StringBuilder, replace named variables into set of {0}, {1}.. etc, And then use old string.Format method, but it's not really scalable and pain in the back to write all the boilerplate.

var formatTemplate = new StringBuilder(template)
    .Replace("{orderNo}", "{0}")
    .Replace("{warehouse}", "{1}")
    .Replace("{eta","{2")
    .ToString();
return string.Format(template, orderNo, warehouse, eta)

Is there a better way to do it? Maybe a way to read the template as interpolated string? The string is read from an outside source so I cannot simply use the $"{marker}" mechanic.

DemoBytom
  • 305
  • 1
  • 9
  • Why it's pain and why it's a lot of boilerplate code? – Tim Schmelter Mar 16 '18 at 12:44
  • 1
    Because they care what messages are being sent, from the system, to clients, and want to be able to define them, outside of the system. And sometimes they want to change them, in some languages markers are in different order, and handling a set of numbers is confusing then, I guess. And pain to write the boilerplate is, when you have 20+ markers, per template, and have to replace them all in correct order. – DemoBytom Mar 16 '18 at 12:49
  • There's a suggestion in another question to try `.Aggregate()`: https://stackoverflow.com/a/36778184/1462295 – BurnsBA Mar 16 '18 at 13:07
  • Also maybe Phil Haack's solution mentioned in https://stackoverflow.com/a/159126/1462295 – BurnsBA Mar 16 '18 at 13:12

2 Answers2

2

Please check out SmartFormat which in effect will do what you want to.

(You sayed, that you do not use replacing the human readable names with the indexes)

Example from their wiki:

String.Format references all args by index:

String.Format("{0} {1}", person.FirstName, person.LastName)

Smart.Format takes this a step further, and lets you use named placeholders instead:

Smart.Format("{FirstName} {LastName}", person)

In fact, Smart.Format supports several kinds of expressions:

Smart.Format("{FirstName.ToUpper} {LastName.ToLower}", person)

Working example requires the variables to be packed into an anonymous type:

var formatted=Smart.Format(template, new { orderNo, warehouseName, eta })

that returns correct, desired, result, with markers correctly replaced. Without the anonymous type it didn't seem to work.

DemoBytom
  • 305
  • 1
  • 9
dsdel
  • 1,042
  • 1
  • 8
  • 11
  • SmartFormat is an external library, you can view examples at their wiki for [named placeholders](https://github.com/scottrippey/SmartFormat.NET/wiki/Expressions-and-Nesting) – dsdel Mar 16 '18 at 12:55
  • @dsdel indeed, but that will make your answer more complete if you provide a sample here, otherwise it counts as a link-only-answer and should really be a comment. – trailmax Mar 16 '18 at 12:58
  • This worked. I have to pack the variables into an anonymous type, but so far it seems to work: `var formatted=Smart.Format(template, new { orderNo, warehouseName, eta })` returns desired, formatted string. Thank you – DemoBytom Mar 16 '18 at 13:10
  • I understand, but at some point this stops being a trivial stuff. When you have 2 templates with 3-4 markers each - it is. When you have dozens of templates, in different languages, with dozens of markers, this might start to be an issue, to manualy write all replaces, and deal with formatting that way. The more I can delegate onto external lib, and just unit test the results, then move onto more important stuf, the better. In this ex. I eventually ended up just doing a models with all the required fields, per template type, passing it into `Smart.Format` and letting it deal with formatting. – DemoBytom Mar 16 '18 at 16:44
-1

You can use string interpolation if your framework supports: Have a look at this: https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/interpolated-strings

You can then update your code something like:

 var orderNo = 1;
 var warehouse = "Trinidad";
 var template = $"Your order {orderNo} has dispatched from {warehouse}";
Praneet Nadkar
  • 823
  • 7
  • 16
  • 2
    The template is read from outside source, so the $"{}" string interpolation won't work, because from what I understand, it needs to be composed during compilation, not during the runtime. – DemoBytom Mar 16 '18 at 12:56
  • Okay. Could you please explain why it wont work. It is not clear to me why it needs to be composed during compilation. The orderNo and warehouse will have the values at runtime. Please correct me if I am wrong and if this is the wrong approach – Praneet Nadkar Mar 16 '18 at 13:02
  • The template is read from otside source. So I don't, ever, have a line `var template = $"Your order {orderNo}.."`, instead it's `var template=_dbProvider.LoadTemplate();`. During runtime, the template variable does have the `"Your order {orderNo}.."` value, but string interpolation requires you to have that assigned in the code. Even if you load `"$\"Your order {orderNo}.."` from DB, it will be treated as regular, not interpolated string. – DemoBytom Mar 16 '18 at 13:17