Skip to content

Commit

Permalink
Plugin support for EvaluatableExpressionFilter
Browse files Browse the repository at this point in the history
Closes #13454
  • Loading branch information
roji committed Feb 26, 2020
1 parent ca43ad1 commit 96dee3a
Show file tree
Hide file tree
Showing 5 changed files with 130 additions and 12 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,7 @@ public static readonly IDictionary<Type, ServiceCharacteristics> CoreServices
{ typeof(ILazyLoader), new ServiceCharacteristics(ServiceLifetime.Transient) },
{ typeof(IParameterBindingFactory), new ServiceCharacteristics(ServiceLifetime.Singleton, multipleRegistrations: true) },
{ typeof(ITypeMappingSourcePlugin), new ServiceCharacteristics(ServiceLifetime.Singleton, multipleRegistrations: true) },
{ typeof(IEvaluatableExpressionFilterPlugin), new ServiceCharacteristics(ServiceLifetime.Singleton, multipleRegistrations: true) },
{ typeof(ISingletonOptions), new ServiceCharacteristics(ServiceLifetime.Singleton, multipleRegistrations: true) },
{ typeof(IConventionSetPlugin), new ServiceCharacteristics(ServiceLifetime.Scoped, multipleRegistrations: true) },
{ typeof(IResettableService), new ServiceCharacteristics(ServiceLifetime.Scoped, multipleRegistrations: true) },
Expand Down
15 changes: 4 additions & 11 deletions src/EFCore/Query/EvaluatableExpressionFilter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ namespace Microsoft.EntityFrameworkCore.Query
/// This service cannot depend on services registered as <see cref="ServiceLifetime.Scoped" />.
/// </para>
/// </summary>
public class EvaluatableExpressionFilter : IEvaluatableExpressionFilter
public class EvaluatableExpressionFilter : EvaluatableExpressionFilterBase
{
// This methods are non-deterministic and result varies based on time of running the query.
// Hence we don't evaluate them. See issue#2069
Expand Down Expand Up @@ -53,11 +53,6 @@ private static readonly MethodInfo _randomNextOneArg
private static readonly MethodInfo _randomNextTwoArgs
= typeof(Random).GetRuntimeMethod(nameof(Random.Next), new[] { typeof(int), typeof(int) });

/// <summary>
/// Parameter object containing dependencies for this service.
/// </summary>
protected virtual EvaluatableExpressionFilterDependencies Dependencies { get; }

/// <summary>
/// <para>
/// Creates a new <see cref="EvaluatableExpressionFilter" /> instance.
Expand All @@ -70,10 +65,8 @@ private static readonly MethodInfo _randomNextTwoArgs
/// <param name="dependencies"> The dependencies to use. </param>
public EvaluatableExpressionFilter(
[NotNull] EvaluatableExpressionFilterDependencies dependencies)
: base(dependencies)
{
Check.NotNull(dependencies, nameof(dependencies));

Dependencies = dependencies;
}

/// <summary>
Expand All @@ -82,7 +75,7 @@ public EvaluatableExpressionFilter(
/// <param name="expression"> The expression. </param>
/// <param name="model"> The model. </param>
/// <returns> True if the expression can be evaluated; false otherwise. </returns>
public virtual bool IsEvaluatableExpression(Expression expression, IModel model)
public override bool IsEvaluatableExpression(Expression expression, IModel model)
{
Check.NotNull(expression, nameof(expression));
Check.NotNull(model, nameof(model));
Expand Down Expand Up @@ -116,7 +109,7 @@ public virtual bool IsEvaluatableExpression(Expression expression, IModel model)
break;
}

return true;
return base.IsEvaluatableExpression(expression, model);
}

public virtual bool IsQueryableFunction(Expression expression, IModel model) => false;
Expand Down
72 changes: 72 additions & 0 deletions src/EFCore/Query/EvaluatableExpressionFilterBase.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
// 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;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using JetBrains.Annotations;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Utilities;
using Microsoft.Extensions.DependencyInjection;

namespace Microsoft.EntityFrameworkCore.Query
{
/// <summary>
/// <para>
/// Represents a filter for evaluatable expressions.
/// </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 EvaluatableExpressionFilterBase : IEvaluatableExpressionFilter
{
/// <summary>
/// Parameter object containing dependencies for this service.
/// </summary>
protected virtual EvaluatableExpressionFilterDependencies Dependencies { get; }

/// <summary>
/// <para>
/// Creates a new <see cref="EvaluatableExpressionFilter" /> instance.
/// </para>
/// <para>
/// This type is typically used by database providers (and other extensions). It is generally
/// not used in application code.
/// </para>
/// </summary>
/// <param name="dependencies"> The dependencies to use. </param>
public EvaluatableExpressionFilterBase(
[NotNull] EvaluatableExpressionFilterDependencies dependencies)
{
Check.NotNull(dependencies, nameof(dependencies));

Dependencies = dependencies;
}

/// <summary>
/// Checks whether the given expression can be evaluated.
/// </summary>
/// <param name="expression"> The expression. </param>
/// <param name="model"> The model. </param>
/// <returns> True if the expression can be evaluated; false otherwise. </returns>
public virtual bool IsEvaluatableExpression(Expression expression, IModel model)
{
Check.NotNull(expression, nameof(expression));
Check.NotNull(model, nameof(model));

foreach (var plugin in Dependencies.Plugins)
{
if (!plugin.IsEvaluatableExpression(expression, model))
{
return false;
}
}

return true;
}
}
}
21 changes: 20 additions & 1 deletion src/EFCore/Query/EvaluatableExpressionFilterDependencies.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
// 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.Collections.Generic;
using JetBrains.Annotations;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Utilities;
using Microsoft.Extensions.DependencyInjection;

namespace Microsoft.EntityFrameworkCore.Query
Expand Down Expand Up @@ -50,8 +53,24 @@ public sealed class EvaluatableExpressionFilterDependencies
/// </para>
/// </summary>
[EntityFrameworkInternal]
public EvaluatableExpressionFilterDependencies()
public EvaluatableExpressionFilterDependencies([NotNull] IEnumerable<IEvaluatableExpressionFilterPlugin> plugins)
{
Check.NotNull(plugins, nameof(plugins));

Plugins = plugins;
}

/// <summary>
/// Gets the plugins.
/// </summary>
public IEnumerable<IEvaluatableExpressionFilterPlugin> Plugins { get; }

/// <summary>
/// Clones this dependency parameter object with one service replaced.
/// </summary>
/// <param name="plugins"> A replacement for the current dependency of this type. </param>
/// <returns> A new parameter object with the given service replaced. </returns>
public EvaluatableExpressionFilterDependencies With([NotNull] IEnumerable<IEvaluatableExpressionFilterPlugin> plugins)
=> new EvaluatableExpressionFilterDependencies(plugins);
}
}
33 changes: 33 additions & 0 deletions src/EFCore/Query/IEvaluatableExpressionFilterPlugin.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// 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.Collections.Generic;
using System.Linq.Expressions;
using JetBrains.Annotations;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.Extensions.DependencyInjection;

namespace Microsoft.EntityFrameworkCore.Query
{
/// <summary>
/// <para>
/// Represents a plugin evaluatable expression filter.
/// </para>
/// <para>
/// The service lifetime is <see cref="ServiceLifetime.Singleton" /> and multiple registrations
/// are allowed. This means a single instance of each service 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 interface IEvaluatableExpressionFilterPlugin
{
/// <summary>
/// Checks whether the given expression can be evaluated.
/// </summary>
/// <param name="expression"> The expression. </param>
/// <param name="model"> The model. </param>
/// <returns> True if the expression can be evaluated; false otherwise. </returns>
bool IsEvaluatableExpression([NotNull] Expression expression, [NotNull] IModel model);
}
}

0 comments on commit 96dee3a

Please sign in to comment.