Repository: dotnet/efcore
File: src/EFCore.Cosmos/Query/Internal/CosmosSqlTranslatingExpressionVisitor.cs (C#)

Code before:
            // Treat type as object for null comparison
            var access = new SqlObjectAccessExpression(entityReference.Object);
            result = sqlExpressionFactory.MakeBinary(
                nodeType,
                access,
                sqlExpressionFactory.Constant(null, typeof(object))!,
                typeMappingSource.FindMapping(typeof(bool)))!;
            return true;
        }

        if (entityType.FindPrimaryKey()?.Properties is not { } primaryKeyProperties)
        {
            throw new InvalidOperationException(
                CoreStrings.EntityEqualityOnKeylessEntityNotSupported(
                    nodeType == ExpressionType.Equal
                        ? equalsMethod ? nameof(object.Equals) : "=="
                        : equalsMethod
                            ? "!" + nameof(object.Equals)
                            : "!=",
                    entityType.DisplayName()));
        }

        if (compareReference is EntityReferenceExpression compareEntityReference)
        {
            // Comparing of 2 different entity types is always false.
            if (entityType.GetRootType() != compareEntityReference.EntityType.GetRootType())
            {
                result = Visit(Expression.Constant(false));
                return true;
            }
        }

        result = Visit(
            primaryKeyProperties.Select(p =>
                    Expression.MakeBinary(
                        nodeType,
                        CreatePropertyAccessExpression(left, p),
                        CreatePropertyAccessExpression(right, p)))
                .Aggregate((l, r) => nodeType == ExpressionType.Equal
                    ? Expression.AndAlso(l, r)
                    : Expression.OrElse(l, r)));

        return true;
    }

    private Expression CreatePropertyAccessExpression(Expression target, IProperty property)
    {
        switch (target)
        {
            case SqlConstantExpression sqlConstantExpression:
                return Expression.Constant(

Review comment (security):
I'm slightly confused here... Unless I'm mistaken, when comparing two different entity types, the result is always false, regardless of whether they have the same root or not. In other words, if I want all customers where `BillingAddress == ShippingAddress`, I will always get nothing back. This sort of odd/unintuitive/non-useful behavior is exactly one of the reasons we're moving from entity type modeling for JSON to complex type, where comparison is structural (compare the contents, not the "identity").

If that's all accurate, then I don't think you need to call GetRootType() above - the moment you have an entity reference on both sides and the IEntityType isn't the same, we can immediately return false. Makes sense?

<details>
<summary>Minimal Cosmos test comparing two entity types with the same root</summary>

```c#
await using var context = new BlogContext();
await context.Database.EnsureDeletedAsync();
await context.Database.EnsureCreatedAsync();

_ = await context.Blogs.Where(b => b.ShippingAddress == b.BillingAddress).ToListAsync();

public class BlogContext : DbContext
{
    public DbSet<Blog> Blogs { get; set; }

    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        => optionsBuilder
            .UseCosmos(
                Environment.GetEnvironmentVariable("CosmosNoSql__ConnectionString")!,
                databaseName: "test",
                o => o
                    .ConnectionMode(ConnectionMode.Gateway)
                    .HttpClientFactory(() => new HttpClient(
                        new HttpClientHandler
                        {
                            ServerCertificateCustomValidationCallback =
                                HttpClientHandler.DangerousAcceptAnyServerCertificateValidator
                        })))
            .LogTo(Console.WriteLine, LogLevel.Information)
            .EnableSensitiveDataLogging();

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Blog>().OwnsOne(b => b.ShippingAddress, b => b.ToJson());
        modelBuilder.Entity<Blog>().OwnsOne(b => b.BillingAddress, b => b.ToJson());
    }
}

public class Blog
{
    public int Id { get; set; }
    public string Name { get; set; }

    public Address ShippingAddress { get; set; }
    public Address BillingAddress { get; set; }
}

public class Address
{
    public string Street { get; set; }
    public string City { get; set; }
}
```

Generated SQL:

```sql
SELECT VALUE c
FROM root c
WHERE false
```

</details>

Code after:
            {
                result = Visit(Expression.Constant(nodeType != ExpressionType.Equal));
                return true;
            }

            var access = Visit(shaper.ValueBufferExpression);
            result = new SqlBinaryExpression(
                nodeType,
                access,
                sqlExpressionFactory.Constant(null, typeof(object), CosmosTypeMapping.Default)!,
                typeof(bool),
                typeMappingSource.FindMapping(typeof(bool)))!;
            return true;
        }

        var leftEntityType = leftEntityReference?.EntityType;
        var rightEntityType = rightEntityReference?.EntityType;
        var entityType = leftEntityType ?? rightEntityType;

        Check.DebugAssert(entityType != null, "At least either side should be entityReference so entityType should be non-null.");

        if (leftEntityType != null
            && rightEntityType != null
            && leftEntityType.GetRootType() != rightEntityType.GetRootType())
        {
            result = sqlExpressionFactory.Constant(false);
            return true;
        }

        var primaryKeyProperties = entityType.FindPrimaryKey()?.Properties;
        if (primaryKeyProperties == null)
        {
            throw new InvalidOperationException(
                CoreStrings.EntityEqualityOnKeylessEntityNotSupported(
                    nodeType == ExpressionType.Equal
                        ? equalsMethod ? nameof(object.Equals) : "=="
                        : equalsMethod
                            ? "!" + nameof(object.Equals)
                            : "!=",
                    entityType.DisplayName()));
        }

        result = Visit(
            primaryKeyProperties.Select(p =>
                    Expression.MakeBinary(
                        nodeType,
                        CreatePropertyAccessExpression(left, p),
                        CreatePropertyAccessExpression(right, p)))
                .Aggregate((l, r) => nodeType == ExpressionType.Equal
                    ? Expression.AndAlso(l, r)
                    : Expression.OrElse(l, r)));