-
Notifications
You must be signed in to change notification settings - Fork 3.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
SqlServer: Translate bool ToString #14205
Comments
"true" or "false". C# representation. It could be achieved via conditional for bool case or for other case where it is not possible it should not try to convert to string on server. Especially in scenarios where there are value converters applied. |
I don't follow. SqlServerObjectToStringTranslator translates |
Then it's a bug. |
You sound uncertain, and I too could be wrong (just because I don't see a "conditional for bool case" doesn't mean it's not there). Could you have someone look at the source code, confirm whether this is indeed a bug and explain what the fix should be? If it's a bug, then there's probably a missing test. |
var query = db.Blogs.Where(b => b.Value.ToString() == "true").ToList();
Console.WriteLine(query.Count); outputs following SQL SELECT [b].[Id], [b].[Value]
FROM [Blogs] AS [b]
WHERE CONVERT(VARCHAR(5), [b].[Value]) = N'true' Which of course produces incorrect results. EF Core 2.2.0 |
What about other types, like DateTime? Should dateTime.ToString() and Convert.ToString(dateTime) also return the C# representation of those strings? |
@smitpatel That's the issue where I pointed out an inconsistency between dateTime.ToString() and Convert.ToString(dateTime), but it does not describe the expected string format. Additionally, DateTimeOffset has a bigger problem, because there's no Convert.ToString(DateTimeOffset) method. You may want to split this up into three separate bugs. For now, I'm looking for guidance regarding expected string formats. |
@MaxG117 - For expected string format you can read discussion here #7364 Essentially, we are not going to do anything other than default unless there is really strong demand for it. #14193 - Will track to make all possible conversions (initiated through instance.ToString or Convert.ToString), work in default format. It is an enhancement. If you want your custom formatter to be used server side, then you can add DbFunction in EF Core which would map to server with your specified formatter and we will translate into it during query. |
Actually I think #6264 is more relevant to my question.
I believe it is not ideal to stop bool values from going to server translation. For example, if a bool is used in a |
Yes, it is not ideal. But doing client eval rather than giving incorrect result is much better. Incorrect results could cause data corruption. |
Why would translating a boolean to a string on the server produce incorrect results? By the way, your example above:
wouldn't work, because I'm suggesting doing everything on the server, so that this query: bool myBool = true;
var query = db.Blogs.Where(b => b.Value.ToString() == myBool.ToString()).ToList(); produces the following SQL: SELECT [b].[Id], [b].[Value]
FROM [Blogs] AS [b]
WHERE [b].[Value] = 1 |
Translating a Boolean to sever in current way produces incorrect results. Stopping it from going to server at least avoid incorrect results. Translating it correctly to server would be most efficient. So is all other pending translation which a lot of customers ask for. At the end of the day, we have limited resources and time. We cannot translate everything to server side in next release. Hence I posted, we can translate correctly to server at some point, but for the next release, we need to stop translating to avoid incorrect result. |
I have a similar issue! Steps to reproduce
public class EntryDocument
{
public bool? IsCompleted { get; private set; }
}
public class SupplyEntryDocumentMap : IEntityTypeConfiguration<EntryDocument>
{
public void Configure(EntityTypeBuilder<EntryDocument> builder)
{
builder.Property(r => r.IsCompleted)
.HasColumnName("Ent_Concluido")
.HasMaxLength(1)
.IsFixedLength()
.HasConversion(new BoolToStringConverter("N", "S")); // Not or Yes in pt-BR ;)
}
}
public class EntryDocumentRepository
{
public IQueryable<EntryDocument> GetByStatus(bool? isCompleted)
{
return from entryDocument in DbSet.AsNoTracking()
where isCompleted == null || (entryDocument.IsCompleted ?? false == isCompleted)
select entryDocument;
}
}
WHERE (COALESCE([entryDocument].[Ent_Concluido], CASE
WHEN N'N' = @__isCompleted_0
THEN CAST(1 AS BIT) ELSE CAST(0 AS BIT)
Exception:
It works normally if not set default value to Further technical detailsEF Core version: 2.2 |
Is this something I can help with? It looks like we need to add some case in SqlServerObjectToStringTranslator, right? |
I added something like this to if (instance.Type.UnwrapNullableType() == typeof(bool))
{
return _sqlExpressionFactory.Case(
new[]
{
new CaseWhenClause(
_sqlExpressionFactory.Equal(instance, _sqlExpressionFactory.Constant(false)),
_sqlExpressionFactory.Constant(false.ToString()))
},
_sqlExpressionFactory.Constant(true.ToString()));
}
else
{
// handle everything else
} |
Yes, thanks @MaxG117 . I was thinking about something similar. |
- Adding boolean.ToString translation via CASE WHEN END - Adding test to cover a new feature Fixes dotnet#14205
I created #24793 for this. I am not sure about the new test location and testing approach (wasn't able to find anything for SqlServerObjectToStringTranslator). @ajcvickers, @smitpatel: please let me know what you think. |
By the way, a similar fix should also go into SqlServerConvertTranslator.cs. In my provider's implementation I added public SqlExpression Translate(SqlExpression instance, MethodInfo method, IReadOnlyList<SqlExpression> arguments)
{
if (_supportedMethods.Contains(method))
{
if (method.Name == nameof(Convert.ToString))
{
if (arguments[0]?.Type == typeof(string))
{
// avoid unnecessarily conversion of string to string
return arguments[0];
}
else if (arguments[0]?.Type == typeof(bool))
{
// Convert a bool to its string representation
return _sqlExpressionFactory.Case(
new[]
{
new CaseWhenClause(
_sqlExpressionFactory.Equal(arguments[0], _sqlExpressionFactory.Constant(false)),
_sqlExpressionFactory.Constant(false.ToString()))
},
_sqlExpressionFactory.Constant(true.ToString()));
}
}
// original return here:
return _sqlExpressionFactory.Function(
"CONVERT",
new[] { _sqlExpressionFactory.Fragment(_typeMapping[method.Name]), arguments[0] },
method.ReturnType);
}
else
{
return null;
}
} |
What's the idea behind converting values to string (using either
object.ToString()
orConvert.ToString(object)
)? Should the resulting string be compatible with the C# or the server-side string representation of the object?For example, a C#
bool
is stored in a SqlServerbit
column. Should conversion to string result in "0" and "1" or "true" and "false"?The text was updated successfully, but these errors were encountered: