Skip to content

Commit

Permalink
add null support to query
Browse files Browse the repository at this point in the history
  • Loading branch information
pwelter34 committed May 19, 2024
1 parent d7abba4 commit a96aa1b
Show file tree
Hide file tree
Showing 6 changed files with 150 additions and 6 deletions.
7 changes: 6 additions & 1 deletion src/MediatR.CommandQuery/Extensions/QueryExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,11 @@ public static IQueryable<T> Filter<T>(this IQueryable<T> query, EntityFilter fil
if (string.IsNullOrWhiteSpace(predicate))
return query;

return query.Where(predicate, parameters);
var config = new ParsingConfig
{
UseParameterizedNamesInDynamicQuery = true,
};

return query.Where(config, predicate, parameters);
}
}
2 changes: 2 additions & 0 deletions src/MediatR.CommandQuery/Queries/EntityFilterOperators.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,6 @@ public static class EntityFilterOperators
public const string GreaterThan = "gt";
public const string GreaterThanOrEqual = "gte";
public const string In = "in";
public const string IsNull = "IsNull";
public const string IsNotNull = "IsNotNull";
}
40 changes: 35 additions & 5 deletions src/MediatR.CommandQuery/Queries/LinqExpressionBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,15 @@ public class LinqExpressionBuilder
{ EntityFilterOperators.LessThan, "<" },
{ EntityFilterOperators.LessThanOrEqual, "<=" },
{ EntityFilterOperators.GreaterThan, ">" },
{ EntityFilterOperators.GreaterThanOrEqual, ">=" }
{ EntityFilterOperators.GreaterThanOrEqual, ">=" },
{ "equals", "==" },
{ "not equals", "!=" },
{ "starts with", EntityFilterOperators.StartsWith },
{ "ends with", EntityFilterOperators.EndsWith },
{ "is null", EntityFilterOperators.IsNull },
{ "is empty", EntityFilterOperators.IsNull },
{ "is not null", EntityFilterOperators.IsNotNull },
{ "is not empty", EntityFilterOperators.IsNotNull },
};

private readonly StringBuilder _expression = new();
Expand Down Expand Up @@ -73,26 +81,48 @@ private void WriteExpression(EntityFilter entityFilter)
var value = entityFilter.Value;

// translate operator
var o = string.IsNullOrWhiteSpace(entityFilter.Operator) ? "==" : entityFilter.Operator;
_operatorMap.TryGetValue(o, out var comparison);
var operatorValue = string.IsNullOrWhiteSpace(entityFilter.Operator) ? "==" : entityFilter.Operator;
_operatorMap.TryGetValue(operatorValue, out var comparison);
if (string.IsNullOrEmpty(comparison))
comparison = o.Trim();
comparison = operatorValue.Trim();

// use function call
var negation = comparison.StartsWith("!") || comparison.StartsWith("not", StringComparison.OrdinalIgnoreCase) ? "!" : string.Empty;

if (comparison.EndsWith(EntityFilterOperators.StartsWith, StringComparison.OrdinalIgnoreCase))
{
_expression.Append(negation).Append(name).Append(".StartsWith(@").Append(index).Append(')');
_values.Add(value);
}
else if (comparison.EndsWith(EntityFilterOperators.EndsWith, StringComparison.OrdinalIgnoreCase))
{
_expression.Append(negation).Append(name).Append(".EndsWith(@").Append(index).Append(')');
_values.Add(value);
}
else if (comparison.EndsWith(EntityFilterOperators.Contains, StringComparison.OrdinalIgnoreCase))
{
_expression.Append(negation).Append(name).Append(".Contains(@").Append(index).Append(')');
_values.Add(value);
}
else if (comparison.EndsWith(EntityFilterOperators.IsNull, StringComparison.OrdinalIgnoreCase))
{
_expression.Append(name).Append(" == NULL");
}
else if (comparison.EndsWith(EntityFilterOperators.IsNotNull, StringComparison.OrdinalIgnoreCase))
{
_expression.Append(name).Append(" != NULL");
}
else if (comparison.EndsWith(EntityFilterOperators.In, StringComparison.OrdinalIgnoreCase))
{
_expression.Append(negation).Append("it.").Append(name).Append(" in @").Append(index);
_values.Add(value);
}
else
{
_expression.Append(name).Append(' ').Append(comparison).Append(" @").Append(index);
_values.Add(value);
}

_values.Add(value);
}

private bool WriteGroup(EntityFilter entityFilter)
Expand Down
1 change: 1 addition & 0 deletions src/MediatR.CommandQuery/Services/ActivityTimer.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Diagnostics;
using System.Text;

namespace MediatR.CommandQuery.Services;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,4 +83,77 @@ public async Task EntityQueryIn()
listResult.Should().NotBeNull();
listResult.Total.Should().Be(2);
}

[Fact]
[Trait("Category", "SqlServer")]
public async Task EntityQueryDescriptionNull()
{
var mediator = ServiceProvider.GetService<IMediator>();
mediator.Should().NotBeNull();

var mapper = ServiceProvider.GetService<IMapper>();
mapper.Should().NotBeNull();

// Query Entity
var entityQuery = new EntityQuery
{
Sort = new List<EntitySort> { new EntitySort { Name = "Updated", Direction = "Descending" } },
Filter = new EntityFilter { Name = "Description", Operator = "IsNull" }
};
var listQuery = new EntityPagedQuery<PriorityReadModel>(MockPrincipal.Default, entityQuery);

var listResult = await mediator.Send(listQuery);
listResult.Should().NotBeNull();
}

[Fact]
[Trait("Category", "SqlServer")]
public async Task EntityQueryDescriptionNOtNull()
{
var mediator = ServiceProvider.GetService<IMediator>();
mediator.Should().NotBeNull();

var mapper = ServiceProvider.GetService<IMapper>();
mapper.Should().NotBeNull();

// Query Entity
var entityQuery = new EntityQuery
{
Sort = new List<EntitySort> { new EntitySort { Name = "Updated", Direction = "Descending" } },
Filter = new EntityFilter { Name = "Description", Operator = "is not null" }
};
var listQuery = new EntityPagedQuery<PriorityReadModel>(MockPrincipal.Default, entityQuery);

var listResult = await mediator.Send(listQuery);
listResult.Should().NotBeNull();
}


[Fact]
[Trait("Category", "SqlServer")]
public async Task EntityQueryMultipleFilters()
{
var mediator = ServiceProvider.GetService<IMediator>();
mediator.Should().NotBeNull();

var mapper = ServiceProvider.GetService<IMapper>();
mapper.Should().NotBeNull();

// Query Entity
var entityQuery = new EntityQuery
{
Sort = new List<EntitySort> { new EntitySort { Name = "Updated", Direction = "Descending" } },
Filter = new EntityFilter
{
Filters = new List<EntityFilter> {
new EntityFilter { Name = "Description", Operator = "is null" },
new EntityFilter { Name = "Name", Operator = "equals", Value = "High" }
}
}
};
var listQuery = new EntityPagedQuery<PriorityReadModel>(MockPrincipal.Default, entityQuery);

var listResult = await mediator.Send(listQuery);
listResult.Should().NotBeNull();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,39 @@ public void FilterContains()
builder.Parameters[0].Should().Be("Berry");
}

[Fact]
public void FilterIsNull()
{
var entityFilter = new EntityFilter
{
Name = "Name",
Operator = EntityFilterOperators.IsNull
};
var builder = new LinqExpressionBuilder();
builder.Build(entityFilter);

builder.Expression.Should().NotBeEmpty();
builder.Expression.Should().Be("Name == NULL");

builder.Parameters.Count.Should().Be(0);
}

[Fact]
public void FilterIsNotNull()
{
var entityFilter = new EntityFilter
{
Name = "Name",
Operator = EntityFilterOperators.IsNotNull
};
var builder = new LinqExpressionBuilder();
builder.Build(entityFilter);

builder.Expression.Should().NotBeEmpty();
builder.Expression.Should().Be("Name != NULL");

builder.Parameters.Count.Should().Be(0);
}

[Fact]
public void FilterIn()
Expand Down

0 comments on commit a96aa1b

Please sign in to comment.