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

Add DisableCollectionNullChecks and EntityFrameworkCompatibilityLayer in the mapper configuration #239

Merged
merged 3 commits into from
Dec 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions src/Gridify/Builder/BaseQueryBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -133,9 +133,9 @@ public TQuery Build(ExpressionSyntax expression)
return ((TQuery)query, false);
}

private static object AddIndexerNullCheck(LambdaExpression mapTarget, object query)
private object AddIndexerNullCheck(LambdaExpression mapTarget, object query)
{
if (GridifyGlobalConfiguration.DisableNullChecks || GridifyGlobalConfiguration.EntityFrameworkCompatibilityLayer)
if (mapper.Configuration.DisableCollectionNullChecks || mapper.Configuration.EntityFrameworkCompatibilityLayer)
return query;

var body = mapTarget.Body;
Expand Down Expand Up @@ -199,7 +199,7 @@ private static object AddIndexerNullCheck(LambdaExpression mapTarget, object que
{
return BuildAlwaysFalseQuery(parameter);
}

if (value is DateTime dateTime)
{
if (mapper.Configuration.DefaultDateTimeKind.HasValue)
Expand All @@ -219,10 +219,10 @@ private static object AddIndexerNullCheck(LambdaExpression mapTarget, object que
{
var strLowerValue = value.ToString()?.ToLower();
value = strLowerValue;
if(!string.IsNullOrEmpty(strLowerValue))

if (!string.IsNullOrEmpty(strLowerValue))
{
body = Expression.Call(body, MethodInfoHelper.GetToLowerMethod());
body = Expression.Call(body, MethodInfoHelper.GetToLowerMethod());
}
}

Expand Down
21 changes: 10 additions & 11 deletions src/Gridify/Builder/LinqQueryBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

namespace Gridify.Builder;

public class LinqQueryBuilder<T>(IGridifyMapper<T> mapper) : BaseQueryBuilder<Expression<Func<T, bool>>, T>(mapper)

Check warning on line 10 in src/Gridify/Builder/LinqQueryBuilder.cs

View workflow job for this annotation

GitHub Actions / Analyze (csharp)

Parameter 'IGridifyMapper<T> mapper' is captured into the state of the enclosing type and its value is also passed to the base constructor. The value might be captured by the base class as well.

Check warning on line 10 in src/Gridify/Builder/LinqQueryBuilder.cs

View workflow job for this annotation

GitHub Actions / Analyze (csharp)

Parameter 'IGridifyMapper<T> mapper' is captured into the state of the enclosing type and its value is also passed to the base constructor. The value might be captured by the base class as well.

Check warning on line 10 in src/Gridify/Builder/LinqQueryBuilder.cs

View workflow job for this annotation

GitHub Actions / Analyze (csharp)

Parameter 'IGridifyMapper<T> mapper' is captured into the state of the enclosing type and its value is also passed to the base constructor. The value might be captured by the base class as well.

Check warning on line 10 in src/Gridify/Builder/LinqQueryBuilder.cs

View workflow job for this annotation

GitHub Actions / Analyze (csharp)

Parameter 'IGridifyMapper<T> mapper' is captured into the state of the enclosing type and its value is also passed to the base constructor. The value might be captured by the base class as well.

Check warning on line 10 in src/Gridify/Builder/LinqQueryBuilder.cs

View workflow job for this annotation

GitHub Actions / Analyze (csharp)

Parameter 'IGridifyMapper<T> mapper' is captured into the state of the enclosing type and its value is also passed to the base constructor. The value might be captured by the base class as well.

Check warning on line 10 in src/Gridify/Builder/LinqQueryBuilder.cs

View workflow job for this annotation

GitHub Actions / Analyze (csharp)

Parameter 'IGridifyMapper<T> mapper' is captured into the state of the enclosing type and its value is also passed to the base constructor. The value might be captured by the base class as well.

Check warning on line 10 in src/Gridify/Builder/LinqQueryBuilder.cs

View workflow job for this annotation

GitHub Actions / Analyze (csharp)

Parameter 'IGridifyMapper<T> mapper' is captured into the state of the enclosing type and its value is also passed to the base constructor. The value might be captured by the base class as well.

Check warning on line 10 in src/Gridify/Builder/LinqQueryBuilder.cs

View workflow job for this annotation

GitHub Actions / Analyze (csharp)

Parameter 'IGridifyMapper<T> mapper' is captured into the state of the enclosing type and its value is also passed to the base constructor. The value might be captured by the base class as well.

Check warning on line 10 in src/Gridify/Builder/LinqQueryBuilder.cs

View workflow job for this annotation

GitHub Actions / build

Parameter 'IGridifyMapper<T> mapper' is captured into the state of the enclosing type and its value is also passed to the base constructor. The value might be captured by the base class as well.

Check warning on line 10 in src/Gridify/Builder/LinqQueryBuilder.cs

View workflow job for this annotation

GitHub Actions / build

Parameter 'IGridifyMapper<T> mapper' is captured into the state of the enclosing type and its value is also passed to the base constructor. The value might be captured by the base class as well.

Check warning on line 10 in src/Gridify/Builder/LinqQueryBuilder.cs

View workflow job for this annotation

GitHub Actions / build

Parameter 'IGridifyMapper<T> mapper' is captured into the state of the enclosing type and its value is also passed to the base constructor. The value might be captured by the base class as well.

Check warning on line 10 in src/Gridify/Builder/LinqQueryBuilder.cs

View workflow job for this annotation

GitHub Actions / build

Parameter 'IGridifyMapper<T> mapper' is captured into the state of the enclosing type and its value is also passed to the base constructor. The value might be captured by the base class as well.

Check warning on line 10 in src/Gridify/Builder/LinqQueryBuilder.cs

View workflow job for this annotation

GitHub Actions / build

Parameter 'IGridifyMapper<T> mapper' is captured into the state of the enclosing type and its value is also passed to the base constructor. The value might be captured by the base class as well.

Check warning on line 10 in src/Gridify/Builder/LinqQueryBuilder.cs

View workflow job for this annotation

GitHub Actions / build

Parameter 'IGridifyMapper<T> mapper' is captured into the state of the enclosing type and its value is also passed to the base constructor. The value might be captured by the base class as well.

Check warning on line 10 in src/Gridify/Builder/LinqQueryBuilder.cs

View workflow job for this annotation

GitHub Actions / build

Parameter 'IGridifyMapper<T> mapper' is captured into the state of the enclosing type and its value is also passed to the base constructor. The value might be captured by the base class as well.

Check warning on line 10 in src/Gridify/Builder/LinqQueryBuilder.cs

View workflow job for this annotation

GitHub Actions / build

Parameter 'IGridifyMapper<T> mapper' is captured into the state of the enclosing type and its value is also passed to the base constructor. The value might be captured by the base class as well.
{
protected override Expression<Func<T, bool>>? BuildNestedQuery(
Expression body, IGMap<T> gMap, ValueExpressionSyntax value, ISyntaxNode op)
Expand Down Expand Up @@ -297,7 +297,7 @@
}
}

private static LambdaExpression ParseMethodCallExpression(MethodCallExpression exp, LambdaExpression predicate, ISyntaxNode op)
private LambdaExpression ParseMethodCallExpression(MethodCallExpression exp, LambdaExpression predicate, ISyntaxNode op)
{
switch (exp.Arguments.First())
{
Expand Down Expand Up @@ -349,7 +349,7 @@
}
}

private static LambdaExpression GetContainsExpression(MemberExpression member, BinaryExpression binaryExpression, ISyntaxNode op)
private LambdaExpression GetContainsExpression(MemberExpression member, BinaryExpression binaryExpression, ISyntaxNode op)
{
var param = GetParameterExpression(member);
var prop = GetPropertyOrField(member, param);
Expand Down Expand Up @@ -393,7 +393,7 @@
};
}

private static LambdaExpression GetAnyExpression(MemberExpression member, Expression predicate)
private LambdaExpression GetAnyExpression(MemberExpression member, Expression predicate)
{
var param = GetParameterExpression(member);
var prop = GetPropertyOrField(member, param);
Expand All @@ -410,12 +410,11 @@
return GetExpressionWithNullCheck(prop, param, anyExp);
}

private static LambdaExpression GetExpressionWithNullCheck(MemberExpression prop, ParameterExpression param, Expression right)
private LambdaExpression GetExpressionWithNullCheck(MemberExpression prop, ParameterExpression param, Expression right)
{
// Entityframework doesn't support NullChecking for Collections (issue #58)
// also issue #70 for NHibernate - and #173
if (GridifyGlobalConfiguration.DisableNullChecks ||
GridifyGlobalConfiguration.EntityFrameworkCompatibilityLayer)
if (mapper.Configuration.DisableCollectionNullChecks || mapper.Configuration.EntityFrameworkCompatibilityLayer)
return Expression.Lambda(right, param);

var nullChecker = Expression.NotEqual(prop, Expression.Constant(null));
Expand All @@ -425,7 +424,7 @@

private Expression GetValueExpression(Type type, object? value)
{
if (!GridifyGlobalConfiguration.EntityFrameworkCompatibilityLayer)
if (!mapper.Configuration.EntityFrameworkCompatibilityLayer)
return Expression.Constant(value, type);

// active parameterized query for EF
Expand All @@ -436,7 +435,7 @@

private BinaryExpression GetLessThanOrEqualExpression(Expression body, ValueExpressionSyntax valueExpression, object? value)
{
if (GridifyGlobalConfiguration.EntityFrameworkCompatibilityLayer)
if (mapper.Configuration.EntityFrameworkCompatibilityLayer)
return Expression.LessThanOrEqual(Expression.Call(null, MethodInfoHelper.GetCompareMethod(), body, GetValueExpression(typeof(string), value)),
Expression.Constant(0));

Expand All @@ -447,7 +446,7 @@

private BinaryExpression GetGreaterThanOrEqualExpression(Expression body, ValueExpressionSyntax valueExpression, object? value)
{
if (GridifyGlobalConfiguration.EntityFrameworkCompatibilityLayer)
if (mapper.Configuration.EntityFrameworkCompatibilityLayer)
return Expression.GreaterThanOrEqual(Expression.Call(null, MethodInfoHelper.GetCompareMethod(), body, GetValueExpression(typeof(string), value)),
Expression.Constant(0));

Expand All @@ -458,7 +457,7 @@

private BinaryExpression GetLessThanExpression(Expression body, ValueExpressionSyntax valueExpression, object? value)
{
if (GridifyGlobalConfiguration.EntityFrameworkCompatibilityLayer)
if (mapper.Configuration.EntityFrameworkCompatibilityLayer)
return Expression.LessThan(Expression.Call(null, MethodInfoHelper.GetCompareMethod(), body, GetValueExpression(typeof(string), value)),
Expression.Constant(0));

Expand All @@ -469,7 +468,7 @@

private BinaryExpression GetGreaterThanExpression(Expression body, ValueExpressionSyntax valueExpression, object? value)
{
if (GridifyGlobalConfiguration.EntityFrameworkCompatibilityLayer)
if (mapper.Configuration.EntityFrameworkCompatibilityLayer)
return Expression.GreaterThan(Expression.Call(null, MethodInfoHelper.GetCompareMethod(), body, GetValueExpression(typeof(string), value)),
Expression.Constant(0));

Expand Down
8 changes: 4 additions & 4 deletions src/Gridify/GridifyGlobalConfiguration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ public static class GridifyGlobalConfiguration
{
/// <summary>
/// It makes the generated expressions compatible
/// whit the entity framework.
/// with the entity framework.
/// Default is false.
/// </summary>
public static bool EntityFrameworkCompatibilityLayer { get; set; }
Expand Down Expand Up @@ -46,14 +46,14 @@ public static class GridifyGlobalConfiguration
/// Default is false
/// </summary>
public static bool DisableNullChecks { get; set; } = false;

/// <summary>
/// By default, string comparison is case sensitive.
/// By default, string comparison is case-sensitive.
/// You can change this behavior by setting this property to true.
/// Default is false
/// </summary>
public static bool CaseInsensitiveFiltering { get; set; } = false;

/// <summary>
/// By default, DateTimeKind.Unspecified is used.
/// You can change this behavior by setting this property to a DateTimeKind value.
Expand Down
22 changes: 19 additions & 3 deletions src/Gridify/GridifyMapperConfiguration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,20 +23,36 @@ public record GridifyMapperConfiguration
/// Default is false
/// </summary>
public bool IgnoreNotMappedFields { get; set; } = GridifyGlobalConfiguration.IgnoreNotMappedFields;

/// <summary>
/// If true, string comparison operations are case insensitive by default.
/// If true, string comparison operations are case-insensitive by default.
/// Default is false
/// </summary>
public bool CaseInsensitiveFiltering { get; set; } = GridifyGlobalConfiguration.CaseInsensitiveFiltering;

/// <summary>
/// By default, DateTimeKind.Unspecified is used.
/// You can change this behavior by setting this property to a DateTimeKind value.
/// Default is null
/// </summary>
public DateTimeKind? DefaultDateTimeKind { get; set; } = GridifyGlobalConfiguration.DefaultDateTimeKind;

/// <summary>
/// On nested collections by default gridify adds null check condition
/// to prevent null reference exceptions, e.g () => field != null && field....
/// some ORMs like NHibernate don't support this.
/// you can disable this behavior by setting this option to true
/// Default is false
/// </summary>
public bool DisableCollectionNullChecks { get; set; } = GridifyGlobalConfiguration.DisableNullChecks;

/// <summary>
/// It makes the generated expressions compatible
/// with the entity framework.
/// Default is false.
/// </summary>
public bool EntityFrameworkCompatibilityLayer { get; set; } = GridifyGlobalConfiguration.EntityFrameworkCompatibilityLayer;

/// <summary>
/// Specifies how field names are inferred from CLR property names.
/// By default, Elastic.Clients.Elasticsearch uses camel-case property names.
Expand Down
19 changes: 18 additions & 1 deletion test/Gridify.Tests/GridifyExtensionsShould.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using Gridify.Syntax;
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Linq.Expressions;
using Xunit;
Expand Down Expand Up @@ -135,7 +136,7 @@ public void ApplyFiltering_NullHandlingUsingCustomConvertor()
Assert.Equal(expected, actual);
Assert.True(actual.Any());
}

[Fact] // issue #220
public void ApplyFiltering_CustomConvertor_ReturningNull_ShouldWork()
{
Expand Down Expand Up @@ -695,6 +696,20 @@ public void ApplyFiltering_UnmappedFields_ShouldSkipWhenIgnored()
Assert.True(actual.Any());
}

[Fact] // issue #238
public void ApplyFiltering_MapperDisableCollectionNullChecks_ShouldPreventAddingNullChecks()
{
var gm = new GridifyMapper<TestClass>(configuration => configuration.DisableCollectionNullChecks = true)
.AddMap("name", q => q.Children.Select(w => w.Name));

#pragma warning disable CS8602 // Dereference of a possibly null reference.
var expected = _fakeRepository.AsQueryable().Where(q => q.Children.Any(w => w.Name.Contains("a"))).ToString();
#pragma warning restore CS8602 // Dereference of a possibly null reference.
var actual = _fakeRepository.AsQueryable().ApplyFiltering("name=*a", gm).ToString();

Assert.Equal(expected, actual);
}

#endregion


Expand Down Expand Up @@ -723,6 +738,8 @@ private class TestOperator : UpperCaseEqual
};

[Fact]
[SuppressMessage("Assertions", "xUnit2031:Do not use Where clause with Assert.Single")]
[SuppressMessage("Assertions", "xUnit2029:Do not use Assert.Empty to check if a value does not exist in a collection")]
public void CustomOperator_GenericRegisterAndRemove_ShouldNotThrowAnyException()
{
GridifyGlobalConfiguration.CustomOperators.Register<TestOperator>();
Expand Down
Loading