Query with Negative multiple AND's generating query with OR (SqlServer)
See original GitHub issueIf I have an query .Where(x => !(x.Name == "Adam" && x.Type == "Person"))
I expect the query generated as NOT (Name = 'Adam' AND Type = 'Person')
but during test this query is generated as Name <> 'Adam' OR Type <> 'Person'
that make the query to exclude wrong result.
If I write the query as .Where(x => x.Name != "Adam" && x.Type != "Person")
or .Where(x => !(x.Name == "Adam") && !(x.Type == "Person"))
the query is translated correctly.
public class TestRunner
{
private readonly Db _db;
public TestRunner(Db db)
{
_db = db;
}
public void Run()
{
_db.TableA.Where(x => !(x.Name == "Adam" && x.Type == "Person")).ToList();
}
}
public class TableA
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
public string Type { get; set; }
}
public class Db : DbContext
{
public Db()
{
}
public Db(DbContextOptions options) : base(options)
{
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
base.OnConfiguring(optionsBuilder);
optionsBuilder.UseSqlServer("Data Source=(local); Integrated Security=True; Initial Catalog=BugTest;MultipleActiveResultSets=True");
}
public DbSet<TableA> TableA { get; set; }
}
Full example of running application exists here https://github.com/Tasteful/bugs/tree/efcore-where-not-problem
Include verbose output
From console log where we can see that the select expression is build correctly and the generated SQL is with OR
instead of AND
.
dbug: Microsoft.EntityFrameworkCore.Query[10107]
Generated query execution expression:
'queryContext => new SingleQueryingEnumerable<TableA>(
(RelationalQueryContext)queryContext,
RelationalCommandCache.SelectExpression(
Projection Mapping:
EmptyProjectionMember -> Dictionary<IProperty, int> { [Property: TableA.Id (int) Required PK AfterSave:Throw ValueGenerated.OnAdd, 0], [Property: TableA.Name (string), 1], [Property: TableA.Type (string), 2], }
SELECT t.Id, t.Name, t.Type
FROM TableA AS t
WHERE Not((t.Name == N'Adam') && (t.Type == N'Person'))),
Func<QueryContext, DbDataReader, ResultContext, SingleQueryResultCoordinator, TableA>,
BugTest.Db,
False,
False
)'
info: Microsoft.EntityFrameworkCore.Database.Command[20101]
Executed DbCommand (3ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
SELECT [t].[Id], [t].[Name], [t].[Type]
FROM [TableA] AS [t]
WHERE (([t].[Name] <> N'Adam') OR [t].[Name] IS NULL) OR (([t].[Type] <> N'Person') OR [t].[Type] IS NULL)
Include provider and version information
EF Core version: 5.0.5 Database provider: Microsoft.EntityFrameworkCore.SqlServer Target framework: .NET 5.0 Operating system: Windows 10 IDE: Visual Studio 2019 16.9.4
Issue Analytics
- State:
- Created 2 years ago
- Comments:9 (9 by maintainers)
I have test now with Linq over a regular
List<>
and directly with TSQL and can see that my assumption was wrong about how&&
is working. Have never recognize that before and probably that is because I try to write queries that not usingNOT
. Then the result will be what I expect anyway.Thanks for clarifying.
@Tasteful on top of what @roji and @smitpatel have already said, you can use Linq to Objects, completely circumventing EF query pipeline and see that it produces the same result:
Perhaps, your example can be demonstrated easier if you have 4 elements in your database:
The query which is not negated:
will return true if entity is simultaneously named Adam and is a person (so returning the first element only). Negation of that MUST return everything else, so it should return elements 2, 3, 4.
This can be done by returning elements whos name is not adam OR who’s type is not person - which is what EF translates the negation to.
They way that you thought the negation should be translated (if I understood correctly), it should only return 3rd element (something that is not named Adam, and is also not a Person). This leads to contradiction, because all 4 elements should be returned if you combine the first query and its negated version, but you only get 2 of them.
If you want to go around de Morgan laws, you need to write the query without parentheses in the way that you want them to behave, rather than putting negation around and rely on EF to optimize it for you.
From EF perspective this issue is by design - we follow all the rules of 2-value logic correctly here.