Skip to content

Commit

Permalink
Fix to #18147 - Where bool column needs to convert to equality when v…
Browse files Browse the repository at this point in the history
…alue converter is applied

Added a postprocessor step that identifies bool columns with value converters inside a predicate and modifies them accordingly.

Fixes #18147
  • Loading branch information
maumar committed Jun 5, 2020
1 parent fb28b56 commit e22e646
Show file tree
Hide file tree
Showing 14 changed files with 1,333 additions and 13 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ public static IServiceCollection AddEntityFrameworkCosmos([NotNull] this IServic
.TryAdd<ISingletonOptions, ICosmosSingletonOptions>(p => p.GetService<ICosmosSingletonOptions>())
.TryAdd<IQueryTranslationPreprocessorFactory, CosmosQueryTranslationPreprocessorFactory>()
.TryAdd<IQueryCompilationContextFactory, CosmosQueryCompilationContextFactory>()
.TryAdd<IQueryTranslationPostprocessorFactory, CosmosQueryTranslationPostprocessorFactory>()
.TryAddProviderSpecificServices(
b => b
.TryAddSingleton<ICosmosSingletonOptions, CosmosSingletonOptions>()
Expand Down
48 changes: 48 additions & 0 deletions src/EFCore.Cosmos/Query/CosmosQueryTranslationPostprocessor.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System.Linq.Expressions;
using JetBrains.Annotations;
using Microsoft.EntityFrameworkCore.Cosmos.Query.Internal;
using Microsoft.EntityFrameworkCore.Query;
using Microsoft.EntityFrameworkCore.Utilities;

namespace Microsoft.EntityFrameworkCore.Cosmos.Query
{
/// <inheritdoc />
public class CosmosQueryTranslationPostprocessor : QueryTranslationPostprocessor
{
/// <summary>
/// Creates a new instance of the <see cref="CosmosQueryTranslationPostprocessor" /> class.
/// </summary>
/// <param name="dependencies"> Parameter object containing dependencies for this class. </param>
/// <param name="sqlExpressionFactory"> The SqlExpressionFactory object to use. </param>
/// <param name="queryCompilationContext"> The query compilation context object to use. </param>
public CosmosQueryTranslationPostprocessor(
[NotNull] QueryTranslationPostprocessorDependencies dependencies,
[NotNull] ISqlExpressionFactory sqlExpressionFactory,
[NotNull] QueryCompilationContext queryCompilationContext)
: base(dependencies, queryCompilationContext)
{
Check.NotNull(dependencies, nameof(dependencies));
Check.NotNull(sqlExpressionFactory, nameof(sqlExpressionFactory));
Check.NotNull(queryCompilationContext, nameof(queryCompilationContext));

SqlExpressionFactory = sqlExpressionFactory;
}

/// <summary>
/// Sql expression factory.
/// </summary>
protected virtual ISqlExpressionFactory SqlExpressionFactory { get; }

/// <inheritdoc />
public override Expression Process(Expression query)
{
query = base.Process(query);
query = new CosmosValueConverterCompensatingExpressionVisitor(SqlExpressionFactory).Visit(query);

return query;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using JetBrains.Annotations;
using Microsoft.EntityFrameworkCore.Cosmos.Query;
using Microsoft.EntityFrameworkCore.Cosmos.Query.Internal;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Utilities;
using Microsoft.Extensions.DependencyInjection;

namespace Microsoft.EntityFrameworkCore.Query
{
/// <summary>
/// <para>
/// Service dependencies parameter class for <see cref="CosmosQueryTranslationPostprocessor" />
/// </para>
/// <para>
/// This type is typically used by database providers (and other extensions). It is generally
/// not used in application code.
/// </para>
/// <para>
/// Do not construct instances of this class directly from either provider or application code as the
/// constructor signature may change as new dependencies are added. Instead, use this type in
/// your constructor so that an instance will be created and injected automatically by the
/// dependency injection container. To create an instance with some dependent services replaced,
/// first resolve the object from the dependency injection container, then replace selected
/// services using the 'With...' methods. Do not call the constructor at any point in this process.
/// </para>
/// <para>
/// The service lifetime is <see cref="ServiceLifetime.Singleton" />. This means a single instance
/// is used by many <see cref="DbContext" /> instances. The implementation must be thread-safe.
/// This service cannot depend on services registered as <see cref="ServiceLifetime.Scoped" />.
/// </para>
/// </summary>
public sealed class CosmosQueryTranslationPostprocessorDependencies
{
/// <summary>
/// <para>
/// Creates the service dependencies parameter object for a <see cref="CosmosQueryTranslationPostprocessor" />.
/// </para>
/// <para>
/// Do not call this constructor directly from either provider or application code as it may change
/// as new dependencies are added. Instead, use this type in your constructor so that an instance
/// will be created and injected automatically by the dependency injection container. To create
/// an instance with some dependent services replaced, first resolve the object from the dependency
/// injection container, then replace selected services using the 'With...' methods. Do not call
/// the constructor at any point in this process.
/// </para>
/// <para>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </para>
/// </summary>
[EntityFrameworkInternal]
public CosmosQueryTranslationPostprocessorDependencies(
[NotNull] ISqlExpressionFactory sqlExpressionFactory)
{
Check.NotNull(sqlExpressionFactory, nameof(sqlExpressionFactory));

SqlExpressionFactory = sqlExpressionFactory;
}

/// <summary>
/// The SQL expression factory.
/// </summary>
public ISqlExpressionFactory SqlExpressionFactory { get; }

/// <summary>
/// Clones this dependency parameter object with one service replaced.
/// </summary>
/// <param name="sqlExpressionFactory"> A replacement for the current dependency of this type. </param>
/// <returns> A new parameter object with the given service replaced. </returns>
public CosmosQueryTranslationPostprocessorDependencies With([NotNull] ISqlExpressionFactory sqlExpressionFactory)
=> new CosmosQueryTranslationPostprocessorDependencies(sqlExpressionFactory);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using JetBrains.Annotations;
using Microsoft.EntityFrameworkCore.Query;
using Microsoft.EntityFrameworkCore.Utilities;
using Microsoft.Extensions.DependencyInjection;

namespace Microsoft.EntityFrameworkCore.Cosmos.Query.Internal
{
/// <summary>
/// <para>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </para>
/// <para>
/// The service lifetime is <see cref="ServiceLifetime.Singleton" />. This means a single instance
/// is used by many <see cref="DbContext" /> instances. The implementation must be thread-safe.
/// This service cannot depend on services registered as <see cref="ServiceLifetime.Scoped" />.
/// </para>
/// </summary>
public class CosmosQueryTranslationPostprocessorFactory : IQueryTranslationPostprocessorFactory
{
private readonly QueryTranslationPostprocessorDependencies _dependencies;
private readonly ISqlExpressionFactory _sqlExpressionFactory;

/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public CosmosQueryTranslationPostprocessorFactory(
[NotNull] QueryTranslationPostprocessorDependencies dependencies,
[NotNull] ISqlExpressionFactory sqlExpressionFactory)
{
_dependencies = dependencies;
_sqlExpressionFactory = sqlExpressionFactory;
}

/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public virtual QueryTranslationPostprocessor Create(QueryCompilationContext queryCompilationContext)
{
Check.NotNull(queryCompilationContext, nameof(queryCompilationContext));

return new CosmosQueryTranslationPostprocessor(
_dependencies,
_sqlExpressionFactory,
queryCompilationContext);
}
}
}
Loading

0 comments on commit e22e646

Please sign in to comment.