I suspect the question can be rephrased to :
Why didn't they provide overloads that accept a FormattableString to pass message templates and parameters using string interpolation syntax, like EF Core does for parameterized queries?
I'd say they got it right. At this point in time using FormattableString offers minimal benefits but creates a lot of confusion.
I just found that Serilog's author explains why this isn't such a good idea even though a semantic logging library looks like a natural fit for this scenario
Semantic Logging
One can argue that FormattableString
would be a great addition to semantic logging libraries like Serilog. In this case an interpolated string does lose important information.
The call
Log.Information("Logged in {UserId}", loggedInUserId);
Won't just log a string based on the template, it will keep the names and values of the parameters and provide them to filters and targets. Wouldn't it be great to get the same result with :
Log.Information($"Logged in {loggedInUserId}");
Serilog's author doesn't think so and explains that :
- A good variable name is not necessarily a good property name
- Holes don’t always have obvious names, eg
Log.Information($"Enabling categories {new[]{1, 2, 3}}");
and concludes that
String interpolation is a great feature, one I’ve looked forward to in C# for a long time. The idea of providing direct support for Serilog is a very interesting one and worth exploring, but I’m increasingly convinced it’s unnecessary.
Interpolation is nice when it keeps code DRY, cutting out the redundant clutter of {0} and {1} and preventing parameter mismatches.
In the case of Serilog, I think it’s incorrect to consider the property names like {UserId} as redundant; in a well-implemented logging strategy they’re an incredibly important part of the picture that deserve their own consideration. You wouldn’t let variable names determine the table and column names in a relational database – it’s exactly the same trade-off being considered here.
Original explanation
That's one of the most controversial features of EF Core actually, and can easily result in the very SQL injection and conversion problems one wants to avoid by using parameters.
This call :
string city = "London";
var londonCustomers = context.Customers
.FromSql($"SELECT * FROM Customers WHERE City = {city}");
calls FromSql(FormattableString)
and will create a parameterized query :
SELECT * FROM Customers WHERE City = @p0
And pass London
as a parameter value.
On the other hand this :
string city = "London";
var query=$"SELECT * FROM Customers WHERE City = {city}";
var londonCustomers = context.Customers.FromSql(query);
calls FromSql(string)
and will generate :
SELECT * FROM Customers WHERE City = London
Which is invalid. It's far too common to fall in this trap even when you do know about the risk.
It doesn't help at all when you already have predefined queries or messages. This usage is far more common in logging, where you (should) use specific message templates defined in well known locations, instead of sprinkling similar looking strings in every log location.
One could argue that this addition made EF Core 2.0 somewhat safer because people had already started using string interpolation in EF Core 1.0, resulting in invalid queries. Adding the FormattableString
overload the EF Core team was able to mitigate that scenario while making it easier to accidentally cause a different problem.
At this point in time the logging designers decided to avoid this confusion. Logging a raw string doesn't have such catastrophic consequences.