Skip to content
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

Querying nested json boolean throws an exception #31071

Closed
Giorgi opened this issue Jun 13, 2023 · 4 comments
Closed

Querying nested json boolean throws an exception #31071

Giorgi opened this issue Jun 13, 2023 · 4 comments

Comments

@Giorgi
Copy link
Contributor

Giorgi commented Jun 13, 2023

I have the following model:

public class Employee
{
    public int Id { get; set; }

    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string Department { get; set; }
    public DateTime DateOfBirth { get; set; }

    public Contact PrimaryContact { get; set; }
}

public class Contact
{
    public string Name { get; set; }
    public string Email { get; set; }
    public string Phone { get; set; }

    public NotificationRules Rules { get; set; }
}

public class NotificationRules
{
    public bool AllowEmail { get; set; }
    public bool AllowCall { get; set; }
    public bool AllowSms { get; set; }
    public int MaximumMessagesPerDay { get; set; }
}

With the following mapping:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Employee>().Property(e => e.LastName).HasMaxLength(50);
    modelBuilder.Entity<Employee>().Property(e => e.FirstName).HasMaxLength(50);
    modelBuilder.Entity<Employee>().Property(e => e.Department).HasMaxLength(50);

    modelBuilder.Entity<Employee>().OwnsOne(e => e.PrimaryContact).ToJson().OwnsOne(builder => builder.Rules);
}

Trying to query on a bool nested property throws an exception but works for int property:

var list = demoContext.Employees.Where(e => e.PrimaryContact.Rules.AllowCall==true).ToList();  //throws
var list = demoContext.Employees.Where(e => e.PrimaryContact.Rules.MaximumMessagesPerDay == 3).ToList(); //works

Here is the exception and trace:

An expression of non-boolean type specified in a context where a condition is expected, near ')'.
   at Microsoft.Data.SqlClient.SqlConnection.OnError(SqlException exception, Boolean breakConnection, Action`1 wrapCloseInAction)
   at Microsoft.Data.SqlClient.TdsParser.ThrowExceptionAndWarning(TdsParserStateObject stateObj, Boolean callerHasConnectionLock, Boolean asyncClose)
   at Microsoft.Data.SqlClient.TdsParser.TryRun(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataReader dataStream, BulkCopySimpleResultSet bulkCopyHandler, TdsParserStateObject stateObj, Boolean& dataReady)
   at Microsoft.Data.SqlClient.SqlDataReader.TryConsumeMetaData()
   at Microsoft.Data.SqlClient.SqlDataReader.get_MetaData()
   at Microsoft.Data.SqlClient.SqlCommand.FinishExecuteReader(SqlDataReader ds, RunBehavior runBehavior, String resetOptionsString, Boolean isInternal, Boolean forDescribeParameterEncryption, Boolean shouldCacheForAlwaysEncrypted)
   at Microsoft.Data.SqlClient.SqlCommand.RunExecuteReaderTds(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, Boolean isAsync, Int32 timeout, Task& task, Boolean asyncWrite, Boolean inRetry, SqlDataReader ds, Boolean describeParameterEncryptionRequest)
   at Microsoft.Data.SqlClient.SqlCommand.RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, TaskCompletionSource`1 completion, Int32 timeout, Task& task, Boolean& usedCache, Boolean asyncWrite, Boolean inRetry, String method)
   at Microsoft.Data.SqlClient.SqlCommand.ExecuteReader(CommandBehavior behavior)
   at Microsoft.Data.SqlClient.SqlCommand.ExecuteDbDataReader(CommandBehavior behavior)
   at Microsoft.EntityFrameworkCore.Storage.RelationalCommand.ExecuteReader(RelationalCommandParameterObject parameterObject)
   at Microsoft.EntityFrameworkCore.Query.Internal.SingleQueryingEnumerable`1.Enumerator.InitializeReader(Enumerator enumerator)
   at Microsoft.EntityFrameworkCore.SqlServer.Storage.Internal.SqlServerExecutionStrategy.Execute[TState,TResult](TState state, Func`3 operation, Func`3 verifySucceeded)
   at Microsoft.EntityFrameworkCore.Query.Internal.SingleQueryingEnumerable`1.Enumerator.MoveNext()
   at System.Collections.Generic.List`1..ctor(IEnumerable`1 collection)
   at System.Linq.Enumerable.ToList[TSource](IEnumerable`1 source)
   at Json.Program.Main(String[] args) in S:\src\EFCore Demos\Json\Program.cs:line 93

Output from logs:

Generated query execution expression:
      'queryContext => new SingleQueryingEnumerable<Employee>(
          (RelationalQueryContext)queryContext,
          RelationalCommandCache.QueryExpression(
              Client Projections:
                  0 -> Dictionary<IProperty, int> { [Property: Employee.Id (int) Required PK AfterSave:Throw ValueGenerated.OnAdd, 0], [Property: Employee.DateOfBirth (DateTime) Required, 1], [Property: Employee.Department (string) Required MaxLength(50), 2], [Property: Employee.FirstName (string) Required MaxLength(50), 3], [Property: Employee.LastName (string) Required MaxLength(50), 4] }
                  1 -> (5, System.Collections.Generic.List`1[System.ValueTuple`2[Metadata.IProperty,System.Int32]], System.String[])
              SELECT e.Id, e.DateOfBirth, e.Department, e.FirstName, e.LastName, JsonScalarExpression(column: e.PrimaryContact, $)
              FROM Employees AS e
              WHERE JsonScalarExpression(column: e.PrimaryContact, $.Rules.AllowCall) == CAST(1 AS bit)),
          null,
          Func<QueryContext, DbDataReader, ResultContext, SingleQueryResultCoordinator, Employee>,
          Json.DemoContext,
          False,
          False,
          True
      )'
13-Jun-23 17:31:59.389 RelationalEventId.ConnectionCreating[20005] (Microsoft.EntityFrameworkCore.Database.Connection)
      Creating DbConnection.
13-Jun-23 17:31:59.447 RelationalEventId.ConnectionCreated[20006] (Microsoft.EntityFrameworkCore.Database.Connection)
      Created DbConnection. (57ms).
13-Jun-23 17:31:59.448 RelationalEventId.CommandCreating[20103] (Microsoft.EntityFrameworkCore.Database.Command)
      Creating DbCommand for 'ExecuteReader'.
13-Jun-23 17:31:59.456 RelationalEventId.CommandCreated[20104] (Microsoft.EntityFrameworkCore.Database.Command)
      Created DbCommand for 'ExecuteReader' (68ms).
13-Jun-23 17:31:59.460 RelationalEventId.CommandInitialized[20106] (Microsoft.EntityFrameworkCore.Database.Command)
      Initialized DbCommand for 'ExecuteReader' (73ms).
13-Jun-23 17:31:59.465 RelationalEventId.ConnectionOpening[20000] (Microsoft.EntityFrameworkCore.Database.Connection)
      Opening connection to database 'EFDemo' on server 'localhost'.
13-Jun-23 17:31:59.926 RelationalEventId.ConnectionOpened[20001] (Microsoft.EntityFrameworkCore.Database.Connection)
      Opened connection to database 'EFDemo' on server 'localhost'.
13-Jun-23 17:31:59.934 RelationalEventId.CommandExecuting[20100] (Microsoft.EntityFrameworkCore.Database.Command)
      Executing DbCommand [Parameters=[], CommandType='Text', CommandTimeout='30']
      SELECT [e].[Id], [e].[DateOfBirth], [e].[Department], [e].[FirstName], [e].[LastName], JSON_QUERY([e].[PrimaryContact],'$')
      FROM [Employees] AS [e]
      WHERE CAST(JSON_VALUE([e].[PrimaryContact],'$.Rules.AllowCall') AS bit)

The generated SQL is clearly missing = 1 at the end.

Include provider and version information

EF Core version:
Database provider: (Microsoft.EntityFrameworkCore.SqlServer 7.0.5)
Target framework: ( .NET 7.0)
Operating system: Windows
IDE: (Visual Studio 2022 17.4)

@ajcvickers
Copy link
Member

Duplicate of #29572

@ajcvickers ajcvickers marked this as a duplicate of #29572 Jun 13, 2023
@ajcvickers ajcvickers closed this as not planned Won't fix, can't repro, duplicate, stale Jun 13, 2023
@Giorgi
Copy link
Contributor Author

Giorgi commented Jun 13, 2023

@ajcvickers The workaround from that ticket doesn't work in this case.

@ajcvickers
Copy link
Member

@Giorgi Try:

var list = context.Employees.Where(e => e.PrimaryContact.Rules.AllowCall == (bool)(object)true).ToList();  //throws

@ajcvickers ajcvickers reopened this Jun 19, 2023
@ajcvickers
Copy link
Member

Note from triage: reopened #29572 for patching.

@ajcvickers ajcvickers closed this as not planned Won't fix, can't repro, duplicate, stale Jul 8, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants