Skip to content

Commit

Permalink
Fix to #19606 - Query: add support for convert to/from bool value usi…
Browse files Browse the repository at this point in the history
…ng Convert.To* methods

Fixes #19606
  • Loading branch information
maumar committed Jun 7, 2020
1 parent 9f316e5 commit a285a2e
Show file tree
Hide file tree
Showing 6 changed files with 200 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ public class SqlServerConvertTranslator : IMethodCallTranslator
{
private static readonly Dictionary<string, string> _typeMapping = new Dictionary<string, string>
{
[nameof(Convert.ToBoolean)] = "bit",
[nameof(Convert.ToByte)] = "tinyint",
[nameof(Convert.ToDecimal)] = "decimal(18, 2)",
[nameof(Convert.ToDouble)] = "float",
Expand All @@ -33,6 +34,7 @@ public class SqlServerConvertTranslator : IMethodCallTranslator

private static readonly List<Type> _supportedTypes = new List<Type>
{
typeof(bool),
typeof(byte),
typeof(DateTime),
typeof(decimal),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -639,6 +639,18 @@ FROM root c
WHERE (c[""Discriminator""] = ""Customer"")");
}

[ConditionalTheory(Skip = "Issue #17246")]
public override async Task Convert_ToBoolean(bool async)
{
await base.Convert_ToBoolean(async);

AssertSql(
@"SELECT c
FROM root c
WHERE ((c[""Discriminator""] = ""Order"") AND (c[""CustomerID""] = ""ALFKI""))");
}


[ConditionalTheory(Skip = "Issue #17246")]
public override async Task Convert_ToByte(bool async)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1063,12 +1063,39 @@ public virtual Task Where_functions_nested(bool async)
entryCount: 91);
}

[ConditionalTheory]
[MemberData(nameof(IsAsyncData))]
public virtual async Task Convert_ToBoolean(bool async)
{
var convertMethods = new List<Expression<Func<Order, bool>>>
{
o => Convert.ToBoolean(Convert.ToBoolean(o.OrderID % 3)),
o => Convert.ToBoolean(Convert.ToByte(o.OrderID % 3)),
o => Convert.ToBoolean(Convert.ToDecimal(o.OrderID % 3)),
o => Convert.ToBoolean(Convert.ToDouble(o.OrderID % 3)),
o => Convert.ToBoolean((float)Convert.ToDouble(o.OrderID % 3)),
o => Convert.ToBoolean(Convert.ToInt16(o.OrderID % 3)),
o => Convert.ToBoolean(Convert.ToInt32(o.OrderID % 3)),
o => Convert.ToBoolean(Convert.ToInt64(o.OrderID % 3)),
};

foreach (var convertMethod in convertMethods)
{
await AssertQuery(
async,
ss => ss.Set<Order>().Where(o => o.CustomerID == "ALFKI")
.Where(convertMethod),
entryCount: 5);
}
}

[ConditionalTheory]
[MemberData(nameof(IsAsyncData))]
public virtual async Task Convert_ToByte(bool async)
{
var convertMethods = new List<Expression<Func<Order, bool>>>
{
o => Convert.ToByte(Convert.ToBoolean(o.OrderID % 1)) >= 0,
o => Convert.ToByte(Convert.ToByte(o.OrderID % 1)) >= 0,
o => Convert.ToByte(Convert.ToDecimal(o.OrderID % 1)) >= 0,
o => Convert.ToByte(Convert.ToDouble(o.OrderID % 1)) >= 0,
Expand All @@ -1095,6 +1122,7 @@ public virtual async Task Convert_ToDecimal(bool async)
{
var convertMethods = new List<Expression<Func<Order, bool>>>
{
o => Convert.ToDecimal(Convert.ToBoolean(o.OrderID % 1)) >= 0,
o => Convert.ToDecimal(Convert.ToByte(o.OrderID % 1)) >= 0,
o => Convert.ToDecimal(Convert.ToDecimal(o.OrderID % 1)) >= 0,
o => Convert.ToDecimal(Convert.ToDouble(o.OrderID % 1)) >= 0,
Expand All @@ -1121,6 +1149,7 @@ public virtual async Task Convert_ToDouble(bool async)
{
var convertMethods = new List<Expression<Func<Order, bool>>>
{
o => Convert.ToDouble(Convert.ToBoolean(o.OrderID % 1)) >= 0,
o => Convert.ToDouble(Convert.ToByte(o.OrderID % 1)) >= 0,
o => Convert.ToDouble(Convert.ToDecimal(o.OrderID % 1)) >= 0,
o => Convert.ToDouble(Convert.ToDouble(o.OrderID % 1)) >= 0,
Expand All @@ -1147,6 +1176,7 @@ public virtual async Task Convert_ToInt16(bool async)
{
var convertMethods = new List<Expression<Func<Order, bool>>>
{
o => Convert.ToInt16(Convert.ToBoolean(o.OrderID % 1)) >= 0,
o => Convert.ToInt16(Convert.ToByte(o.OrderID % 1)) >= 0,
o => Convert.ToInt16(Convert.ToDecimal(o.OrderID % 1)) >= 0,
o => Convert.ToInt16(Convert.ToDouble(o.OrderID % 1)) >= 0,
Expand All @@ -1173,6 +1203,7 @@ public virtual async Task Convert_ToInt32(bool async)
{
var convertMethods = new List<Expression<Func<Order, bool>>>
{
o => Convert.ToInt32(Convert.ToBoolean(o.OrderID % 1)) >= 0,
o => Convert.ToInt32(Convert.ToByte(o.OrderID % 1)) >= 0,
o => Convert.ToInt32(Convert.ToDecimal(o.OrderID % 1)) >= 0,
o => Convert.ToInt32(Convert.ToDouble(o.OrderID % 1)) >= 0,
Expand All @@ -1199,6 +1230,7 @@ public virtual async Task Convert_ToInt64(bool async)
{
var convertMethods = new List<Expression<Func<Order, bool>>>
{
o => Convert.ToInt64(Convert.ToBoolean(o.OrderID % 1)) >= 0,
o => Convert.ToInt64(Convert.ToByte(o.OrderID % 1)) >= 0,
o => Convert.ToInt64(Convert.ToDecimal(o.OrderID % 1)) >= 0,
o => Convert.ToInt64(Convert.ToDouble(o.OrderID % 1)) >= 0,
Expand All @@ -1225,6 +1257,7 @@ public virtual async Task Convert_ToString(bool async)
{
var convertMethods = new List<Expression<Func<Order, bool>>>
{
o => Convert.ToString(Convert.ToBoolean(o.OrderID % 1)) != "10",
o => Convert.ToString(Convert.ToByte(o.OrderID % 1)) != "10",
o => Convert.ToString(Convert.ToDecimal(o.OrderID % 1)) != "10",
o => Convert.ToString(Convert.ToDouble(o.OrderID % 1)) != "10",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -936,13 +936,55 @@ FROM [Customers] AS [c]
WHERE POWER(CAST(CAST(LEN([c].[CustomerID]) AS int) AS float), 2.0E0) = 25.0E0");
}

public override async Task Convert_ToBoolean(bool async)
{
await base.Convert_ToBoolean(async);

AssertSql(
@"SELECT [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate]
FROM [Orders] AS [o]
WHERE ([o].[CustomerID] = N'ALFKI') AND (CONVERT(bit, CONVERT(bit, [o].[OrderID] % 3)) = CAST(1 AS bit))",
//
@"SELECT [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate]
FROM [Orders] AS [o]
WHERE ([o].[CustomerID] = N'ALFKI') AND (CONVERT(bit, CONVERT(tinyint, [o].[OrderID] % 3)) = CAST(1 AS bit))",
//
@"SELECT [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate]
FROM [Orders] AS [o]
WHERE ([o].[CustomerID] = N'ALFKI') AND (CONVERT(bit, CONVERT(decimal(18, 2), [o].[OrderID] % 3)) = CAST(1 AS bit))",
//
@"SELECT [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate]
FROM [Orders] AS [o]
WHERE ([o].[CustomerID] = N'ALFKI') AND (CONVERT(bit, CONVERT(float, [o].[OrderID] % 3)) = CAST(1 AS bit))",
//
@"SELECT [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate]
FROM [Orders] AS [o]
WHERE ([o].[CustomerID] = N'ALFKI') AND (CONVERT(bit, CAST(CONVERT(float, [o].[OrderID] % 3) AS real)) = CAST(1 AS bit))",
//
@"SELECT [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate]
FROM [Orders] AS [o]
WHERE ([o].[CustomerID] = N'ALFKI') AND (CONVERT(bit, CONVERT(smallint, [o].[OrderID] % 3)) = CAST(1 AS bit))",
//
@"SELECT [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate]
FROM [Orders] AS [o]
WHERE ([o].[CustomerID] = N'ALFKI') AND (CONVERT(bit, CONVERT(int, [o].[OrderID] % 3)) = CAST(1 AS bit))",
//
@"SELECT [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate]
FROM [Orders] AS [o]
WHERE ([o].[CustomerID] = N'ALFKI') AND (CONVERT(bit, CONVERT(bigint, [o].[OrderID] % 3)) = CAST(1 AS bit))");
}

public override async Task Convert_ToByte(bool async)
{
await base.Convert_ToByte(async);

AssertSql(
@"SELECT [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate]
FROM [Orders] AS [o]
WHERE ([o].[CustomerID] = N'ALFKI') AND (CONVERT(tinyint, CONVERT(bit, [o].[OrderID] % 1)) >= CAST(0 AS tinyint))",
//
@"SELECT [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate]
FROM [Orders] AS [o]
WHERE ([o].[CustomerID] = N'ALFKI') AND (CONVERT(tinyint, CONVERT(tinyint, [o].[OrderID] % 1)) >= CAST(0 AS tinyint))",
//
@"SELECT [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate]
Expand Down Expand Up @@ -981,6 +1023,10 @@ public override async Task Convert_ToDecimal(bool async)
AssertSql(
@"SELECT [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate]
FROM [Orders] AS [o]
WHERE ([o].[CustomerID] = N'ALFKI') AND (CONVERT(decimal(18, 2), CONVERT(bit, [o].[OrderID] % 1)) >= 0.0)",
//
@"SELECT [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate]
FROM [Orders] AS [o]
WHERE ([o].[CustomerID] = N'ALFKI') AND (CONVERT(decimal(18, 2), CONVERT(tinyint, [o].[OrderID] % 1)) >= 0.0)",
//
@"SELECT [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate]
Expand Down Expand Up @@ -1019,6 +1065,10 @@ public override async Task Convert_ToDouble(bool async)
AssertSql(
@"SELECT [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate]
FROM [Orders] AS [o]
WHERE ([o].[CustomerID] = N'ALFKI') AND (CONVERT(float, CONVERT(bit, [o].[OrderID] % 1)) >= 0.0E0)",
//
@"SELECT [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate]
FROM [Orders] AS [o]
WHERE ([o].[CustomerID] = N'ALFKI') AND (CONVERT(float, CONVERT(tinyint, [o].[OrderID] % 1)) >= 0.0E0)",
//
@"SELECT [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate]
Expand Down Expand Up @@ -1057,6 +1107,10 @@ public override async Task Convert_ToInt16(bool async)
AssertSql(
@"SELECT [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate]
FROM [Orders] AS [o]
WHERE ([o].[CustomerID] = N'ALFKI') AND (CONVERT(smallint, CONVERT(bit, [o].[OrderID] % 1)) >= CAST(0 AS smallint))",
//
@"SELECT [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate]
FROM [Orders] AS [o]
WHERE ([o].[CustomerID] = N'ALFKI') AND (CONVERT(smallint, CONVERT(tinyint, [o].[OrderID] % 1)) >= CAST(0 AS smallint))",
//
@"SELECT [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate]
Expand Down Expand Up @@ -1095,6 +1149,10 @@ public override async Task Convert_ToInt32(bool async)
AssertSql(
@"SELECT [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate]
FROM [Orders] AS [o]
WHERE ([o].[CustomerID] = N'ALFKI') AND (CONVERT(int, CONVERT(bit, [o].[OrderID] % 1)) >= 0)",
//
@"SELECT [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate]
FROM [Orders] AS [o]
WHERE ([o].[CustomerID] = N'ALFKI') AND (CONVERT(int, CONVERT(tinyint, [o].[OrderID] % 1)) >= 0)",
//
@"SELECT [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate]
Expand Down Expand Up @@ -1133,6 +1191,10 @@ public override async Task Convert_ToInt64(bool async)
AssertSql(
@"SELECT [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate]
FROM [Orders] AS [o]
WHERE ([o].[CustomerID] = N'ALFKI') AND (CONVERT(bigint, CONVERT(bit, [o].[OrderID] % 1)) >= CAST(0 AS bigint))",
//
@"SELECT [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate]
FROM [Orders] AS [o]
WHERE ([o].[CustomerID] = N'ALFKI') AND (CONVERT(bigint, CONVERT(tinyint, [o].[OrderID] % 1)) >= CAST(0 AS bigint))",
//
@"SELECT [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate]
Expand Down Expand Up @@ -1171,6 +1233,10 @@ public override async Task Convert_ToString(bool async)
AssertSql(
@"SELECT [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate]
FROM [Orders] AS [o]
WHERE ([o].[CustomerID] = N'ALFKI') AND (CONVERT(nvarchar(max), CONVERT(bit, [o].[OrderID] % 1)) <> N'10')",
//
@"SELECT [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate]
FROM [Orders] AS [o]
WHERE ([o].[CustomerID] = N'ALFKI') AND (CONVERT(nvarchar(max), CONVERT(tinyint, [o].[OrderID] % 1)) <> N'10')",
//
@"SELECT [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate]
Expand Down
86 changes: 84 additions & 2 deletions test/EFCore.SqlServer.FunctionalTests/Query/QueryBugsTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4308,8 +4308,8 @@ public virtual void Correlated_subquery_with_owned_navigation_being_compared_to_
Assert.Null(t.Turnovers);
});

AssertSql(
@"SELECT [p].[Id], CAST(0 AS bit), [a].[Turnovers_AmountIn], [a].[Id]
AssertSql(
@"SELECT [p].[Id], CAST(0 AS bit), [a].[Turnovers_AmountIn], [a].[Id]
FROM [Partners] AS [p]
LEFT JOIN [Address13157] AS [a] ON [p].[Id] = [a].[Partner13157Id]
ORDER BY [p].[Id], [a].[Id]");
Expand Down Expand Up @@ -7277,6 +7277,88 @@ protected override void OnModelCreating(ModelBuilder modelBuilder)

#endregion

[ConditionalFact]
public void Fubarson()
{
using (var ctx = new MyContext())
{
ctx.Database.EnsureDeleted();
ctx.Database.EnsureCreated();

var m1 = new Manufacturer { Name = "m1" };
var m2 = new Manufacturer { Name = "m2" };

var c11 = new Car { Manufacturer = m1, Name = "c11" };
var c12 = new Car { Manufacturer = m1, Name = "c12" };
var c21 = new Car { Manufacturer = m2, Name = "c21" };
var c22 = new Car { Manufacturer = m2, Name = "c22" };
var c23 = new Car { Manufacturer = m2, Name = "c23" };

ctx.Cars.AddRange(c11, c12, c21, c22, c23);
ctx.Manufacturers.AddRange(m1, m2);
ctx.SaveChanges();
}

using (var ctx = new MyContext())
{
var query1 = (from c in ctx.Cars
join m in ctx.Manufacturers on c.ManufacturerId equals m.Id into grouping
from m in grouping.DefaultIfEmpty()
select new { c, m }).ToList().Select(x => x.c);

var query2 = (from c in ctx.Cars.AsNoTracking()
join m in ctx.Manufacturers on c.ManufacturerId equals m.Id into grouping
from m in grouping.DefaultIfEmpty()
select new { c, m }).ToList().Select(x => Fixup(x.c, x.m));

var query3 = (from c in ctx.Cars.AsNoTracking()
join m in ctx.Manufacturers on c.ManufacturerId equals m.Id into grouping
from m in grouping.DefaultIfEmpty()
select new { c, m }).ToList().Select(x => { x.c.Manufacturer = x.m; x.c.ManufacturerId = x.m?.Id ?? 0; return x.c; });
}
}

private Car Fixup(Car c, Manufacturer m)
{
c.Manufacturer = m;
c.ManufacturerId = m?.Id ?? 0;

return c;
}

public class Car
{
public int Id { get; set; }
public string Name { get; set; }
public int ManufacturerId { get; set; }
public Manufacturer Manufacturer { get; set; }
}

public class Manufacturer
{
public int Id { get; set; }
public string Name { get; set; }
public List<Car> Cars { get; set; }
}

public class MyContext : DbContext
{
public DbSet<Car> Cars { get; set; }
public DbSet<Manufacturer> Manufacturers { get; set; }

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Manufacturer>().HasQueryFilter(m => m.Name == "m1");
}

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=Repro;Trusted_Connection=True;MultipleActiveResultSets=true");
}
}



private DbContextOptions _options;

private SqlServerTestStore CreateTestStore<TContext>(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ public NorthwindFunctionsQuerySqliteTest(NorthwindQuerySqliteFixture<NoopModelCu
//Fixture.TestSqlLoggerFactory.SetTestOutputHelper(testOutputHelper);
}

public override Task Convert_ToBoolean(bool async)
=> AssertTranslationFailed(() => base.Convert_ToBoolean(async));

public override Task Convert_ToByte(bool async)
=> AssertTranslationFailed(() => base.Convert_ToByte(async));

Expand Down

0 comments on commit a285a2e

Please sign in to comment.