Skip to content

Commit

Permalink
Merge pull request #42 from Evodim/fixes/Query-extension-In-not-scoped
Browse files Browse the repository at this point in the history
Fixes/query extension in not scoped
  • Loading branch information
medevod authored Feb 9, 2023
2 parents 61698e1 + 5a0ab1f commit a60c18d
Show file tree
Hide file tree
Showing 10 changed files with 189 additions and 30 deletions.
10 changes: 10 additions & 0 deletions Azure.EntityServices.sln
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TableClient.DependencyInjec
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TableClient.DependencyInjection.ProjectionSample", "samples\TableClient.DependencyInjection.ProjectionSample\TableClient.DependencyInjection.ProjectionSample.csproj", "{321BB55F-AA1C-4973-9315-7323D4FC19B0}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{D449467E-9380-4589-BEA2-D6D4BBC20816}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{B6C4E5FE-96DE-4BDF-9D98-2288E601D08C}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -114,11 +118,17 @@ Global
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{F529F568-9D6F-401E-9F5E-2392C7EF65CD} = {D449467E-9380-4589-BEA2-D6D4BBC20816}
{32A4F986-5049-4A87-81A4-7DA7A68E2A8D} = {B6C4E5FE-96DE-4BDF-9D98-2288E601D08C}
{713D44EB-63CD-4B78-8F8A-DAEAA55F377E} = {B6C4E5FE-96DE-4BDF-9D98-2288E601D08C}
{4019B11A-8481-4BE3-8B61-01F9FD0AEC5E} = {B6C4E5FE-96DE-4BDF-9D98-2288E601D08C}
{1D6ED178-F026-4F39-9FC2-FBD1EB182B7B} = {B6C4E5FE-96DE-4BDF-9D98-2288E601D08C}
{1AE5DF7B-3328-49B9-A455-D609ACC2BB95} = {12E4FDA9-194D-4758-BB02-1619C2956738}
{ADAFA655-104A-4375-BA08-3A8007860B5A} = {12E4FDA9-194D-4758-BB02-1619C2956738}
{61ED3E91-9ED3-4D09-A51C-83ABA1F83D75} = {12E4FDA9-194D-4758-BB02-1619C2956738}
{5CEB65EF-0D90-493C-9A81-270B3914F3C4} = {12E4FDA9-194D-4758-BB02-1619C2956738}
{9DF46286-6193-4A5D-A127-866C76252419} = {12E4FDA9-194D-4758-BB02-1619C2956738}
{6D5B1034-2ED7-4EBE-BC4E-6C93CBDCDED6} = {B6C4E5FE-96DE-4BDF-9D98-2288E601D08C}
{BAB3206B-D2F7-4790-8629-76D7A33DC969} = {12E4FDA9-194D-4758-BB02-1619C2956738}
{8F8165C7-37ED-42C2-9D8A-BBABB461BCB8} = {12E4FDA9-194D-4758-BB02-1619C2956738}
{C825B333-1737-45F2-ADD3-2BF7D735BB41} = {12E4FDA9-194D-4758-BB02-1619C2956738}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@ public abstract class InstructionsProviderBase : IQueryInstructionsProvider
{
public virtual string Get(string instruction)
{
if (instruction == null) return string.Empty;
if (string.IsNullOrEmpty(instruction))
{
return string.Empty;
}
var type = GetType();
var value = type.GetProperty(instruction)?.GetValue(this) as string;
return value;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ protected virtual string Build(IFilterExpression<T> expression)
{
if (expression == null) return string.Empty;
StringBuilder queryBuilder = new();

if (expression.PropertyName != null)
{
var strExpression = ExpressionFilterConverter(expression);
Expand All @@ -29,14 +30,13 @@ protected virtual string Build(IFilterExpression<T> expression)
if (expression.Group.Count > 0)
{
foreach (var operation in expression.Group)
{
if (!string.IsNullOrEmpty(InstructionsProvider.Get(operation.GroupOperator))) queryBuilder.Append($" {InstructionsProvider.Get(operation.GroupOperator)} (");
{
queryBuilder.Append($" {InstructionsProvider.Get(operation.GroupOperator)} (");
queryBuilder.Append(Build(operation));
if (!string.IsNullOrEmpty(InstructionsProvider.Get(operation.GroupOperator))) queryBuilder.Append(")");
queryBuilder.Append(")");
}
}
if (!string.IsNullOrEmpty(expression.Operator))
queryBuilder.Append($" {InstructionsProvider.Get(expression.Operator)} ");
}
queryBuilder.Append($" {InstructionsProvider.Get(expression.Operator)} ");
queryBuilder.Append(Build(expression.NextOperation));

return queryBuilder.ToString().Trim();
Expand Down
7 changes: 5 additions & 2 deletions src/Azure.EntityServices.Queries/FilterExpression.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ public virtual IFilterExpression<T> Factory<P>()
public List<IFilterExpression<T>> Group { get; } = new List<IFilterExpression<T>>();

public IFilterExpression<T> NextOperation { get; set; }
public IFilterExpression<T> LastOperation { get; set; }

public IQueryFilter<T, P> AddOperator<P>(string expressionOperator, Expression<Func<T, P>> property)
{
Expand All @@ -39,7 +40,8 @@ public IQueryFilter<T, P> AddOperator<P>(string expressionOperator, Expression<F

newOperation.PropertyName = prop.Name;
newOperation.PropertyType = prop.PropertyType;

newOperation.LastOperation = this;

NextOperation = newOperation;
return newOperation as IQueryFilter<T,P>;
}
Expand All @@ -51,7 +53,8 @@ public IQueryFilter<T> AddOperator(string expressionOperator, string property)

newOperation.PropertyName = property;
newOperation.PropertyType = typeof(object);

newOperation.LastOperation = this;

NextOperation = newOperation;
return newOperation;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml.XPath;

namespace Azure.EntityServices.Queries
{
Expand All @@ -23,8 +24,27 @@ public static IFilterOperator<T> WithEach<T, U>(this IFilterOperator<T> query, I
dynamicQuery = action(item, dynamicQuery);
}
return dynamicQuery;
}
}
/// <summary>
/// Build a filter to check if current field value was not present in given list
/// </summary>
/// <typeparam name="T"></typeparam>
/// <typeparam name="P"></typeparam>
/// <param name="query"></param>
/// <param name="values"></param>
/// <returns></returns>
public static IFilterOperator<T> NotIn<T, P>(this IQueryFilter<T, P> query, params P[] values)
{
var exp = query as IFilterExpression<T>;

exp.LastOperation.AddGroupExpression(string.IsNullOrEmpty(exp.LastOperation.PropertyName) ? "" : "And",
p => p.Where(exp.PropertyName)
._NotIn(values));
exp.PropertyName = null;
exp.Operator = null;

return exp.LastOperation;
}
/// <summary>
/// Build a filter to check if current field value was present in given list
/// </summary>
Expand All @@ -34,33 +54,44 @@ public static IFilterOperator<T> WithEach<T, U>(this IFilterOperator<T> query, I
/// <param name="values"></param>
/// <returns></returns>
public static IFilterOperator<T> In<T, P>(this IQueryFilter<T, P> query, params P[] values)
{

var exp = query as IFilterExpression<T>;

exp.LastOperation.AddGroupExpression(string.IsNullOrEmpty(exp.LastOperation.PropertyName) ? "" : "And",
p => p.Where(exp.PropertyName)
._In(values));
exp.PropertyName = null;
exp.Operator = null;

return exp.LastOperation;
}

private static IFilterOperator<T> _In<T, P>(this IQueryFilter<T> query, P[] values)
{
IQuery<T> nextQuery = (IQuery<T>)query;
foreach (var item in values.SkipLast(1))
{
nextQuery = (IQuery<T>)(nextQuery as IQueryFilter<T>).Equal(item).Or(query.PropertyName);
nextQuery = (IQuery<T>)(nextQuery as IQueryFilter<T>)
.Equal(item)
.Or((query as IFilterExpression<T>).PropertyName);
}
(nextQuery as IQueryFilter<T>).Equal(values.Last());
return nextQuery as IFilterOperator<T>;
}

/// <summary>
/// Build a filter to check if current field value was not present in given list
/// </summary>
/// <typeparam name="T"></typeparam>
/// <typeparam name="P"></typeparam>
/// <param name="query"></param>
/// <param name="values"></param>
/// <returns></returns>
public static IFilterOperator<T> NotIn<T, P>(this IQueryFilter<T, P> query, params P[] values)

private static IFilterOperator<T> _NotIn<T, P>(this IQueryFilter<T> query, P[] values)
{
IQuery<T> nextQuery = (IQuery<T>)query;
foreach (var item in values.SkipLast(1))
{
nextQuery = (IQuery<T>)(nextQuery as IQueryFilter<T>).NotEqual(item).And(query.PropertyName);
nextQuery = (IQuery<T>)(nextQuery as IQueryFilter<T>)
.NotEqual(item)
.And((query as IFilterExpression<T>).PropertyName);
}
(nextQuery as IQueryFilter<T>).NotEqual(values.Last());
return nextQuery as IFilterOperator<T>;
}

}
}
1 change: 1 addition & 0 deletions src/Azure.EntityServices.Queries/IQueryExpression.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,6 @@ public interface IFilterExpression<T> : IFilterOperator<T>, IQueryFilter<T>, IQu
List<IFilterExpression<T>> Group { get; }
public string GroupOperator { get; set; }
IFilterExpression<T> NextOperation { get; set; }
IFilterExpression<T> LastOperation { get; set; }
}
}
3 changes: 1 addition & 2 deletions src/Azure.EntityServices.Queries/IQueryFilter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,7 @@ public interface IQueryFilter<T> : IQueryFilter<T, object>

public interface IQueryFilter<T, P>
{
string PropertyName { get; }


IFilterOperator<T> AddFilterCondition(string comparison, P value);

IFilterOperator<T> AddFilterCondition(string comparison, object value, Type type);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using Azure.EntityServices.Queries;
using Azure.EntityServices.Tables.Core;
using System.Linq;

namespace Azure.EntityServices.Tables
{
Expand Down Expand Up @@ -55,6 +56,6 @@ public static IFilterOperator<T> LessThanOrEqual<T, P>(this ITagQueryFilter<T> q
.GreaterThan($"~{query.TagName}-")
.AndRowKey()
.LessThan($"{TableQueryHelper.ToTagRowKeyPrefix(query.TagName, value)}~");
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -177,8 +177,10 @@ public void Should_BuildGroup_Dynamic_Query_Expression_In_Extension_Helper()

dynamicQuery = builder.Query
.Where(p => p.TenantId).Equal("50")
.And("LastName")
.In("Doe", "Kent");
.And(p=>p
.Where("LastName")
.In("Doe", "Kent"));


queryStr = builder.Build();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,8 @@ public void Should_Use_Tag_GreaterThanOrEqual_Extension()
var builder = new TableStorageQueryBuilder<PersonEntity>(new TagFilterExpression<PersonEntity>());

(builder.Query as TagFilterExpression<PersonEntity>)
.WhereTag("Created").GreaterThanOrEqual("2022-10-22")
.WhereTag("Created")
.GreaterThanOrEqual("2022-10-22")
.And(p => p.TenantId).Equal("10");

var queryStr = builder.Build();
Expand All @@ -86,7 +87,8 @@ public void Should_Use_Tag_LessThanOrEqual_Extension()
var builder = new TableStorageQueryBuilder<PersonEntity>(new TagFilterExpression<PersonEntity>());

(builder.Query as TagFilterExpression<PersonEntity>)
.WhereTag("Created").LessThanOrEqual("2022-10-22")
.WhereTag("Created")
.LessThanOrEqual("2022-10-22")
.And(p => p.TenantId).Equal("10");

var queryStr = builder.Build();
Expand Down Expand Up @@ -197,13 +199,120 @@ public void Should_Use_IncludeTags_To_Get_All_Entities_Included_All_Tags()

(builder.Query as TagFilterExpression<PersonEntity>)
.IncludeTags()
.WherePartitionKey().Equal("tenant1");
.WherePartitionKey()
.Equal("tenant1");

var queryStr = builder.Build();

queryStr.Trim()
.Should()
.Be("PartitionKey eq 'tenant1'");
}

[TestMethod]
public void Should_Use_In_Filter_With_Tag_Prop()
{
var builder = new TableStorageQueryBuilder<PersonEntity>(new TagFilterExpression<PersonEntity>());

(builder.Query as TagFilterExpression<PersonEntity>)
.Where("PartitionKey")
.In("value1", "value2", "value3");

var queryStr = builder.Build();
queryStr.Trim()
.Should()
.Be("(PartitionKey eq 'value1' or PartitionKey eq 'value2' or PartitionKey eq 'value3')");
}

[TestMethod]
public void Should_Use_In_Filter_Inside_ExpressionFilter_Prop2()
{
var builder = new TableStorageQueryBuilder<PersonEntity>(new TagFilterExpression<PersonEntity>());

(builder.Query as TagFilterExpression<PersonEntity>)
.Where("prop1s")
.Equal("value")
.And("tenant1")
.In("tag1", "tag2", "tag3")
.And("prop2")
.Equal("newValue");

var queryStr = builder.Build();

queryStr.Trim()
.Should()
.Be("prop1s eq 'value' and (tenant1 eq 'tag1' or tenant1 eq 'tag2' or tenant1 eq 'tag3') and prop2 eq 'newValue'");
}

[TestMethod]
public void Should_Use_In_Filter_At_Start_Of_ExpressionFilter_Prop2()
{
var builder = new TableStorageQueryBuilder<PersonEntity>(new TagFilterExpression<PersonEntity>());

(builder.Query as TagFilterExpression<PersonEntity>)
.Where("tenant1")
.In("tag1", "tag2", "tag3")
.And("prop2")
.Equal("newValue");

var queryStr = builder.Build();

queryStr.Trim()
.Should()
.Be("(tenant1 eq 'tag1' or tenant1 eq 'tag2' or tenant1 eq 'tag3') and prop2 eq 'newValue'");
}

[TestMethod]
public void Should_Use_NotIn_Filter_With_Tag_Prop()
{
var builder = new TableStorageQueryBuilder<PersonEntity>(new TagFilterExpression<PersonEntity>());

(builder.Query as TagFilterExpression<PersonEntity>)
.Where("PartitionKey")
.NotIn("value1", "value2", "value3");

var queryStr = builder.Build();
queryStr.Trim()
.Should()
.Be("(PartitionKey ne 'value1' and PartitionKey ne 'value2' and PartitionKey ne 'value3')");
}

[TestMethod]
public void Should_Use_NotIn_Filter_Inside_ExpressionFilter_Prop2()
{
var builder = new TableStorageQueryBuilder<PersonEntity>(new TagFilterExpression<PersonEntity>());

(builder.Query as TagFilterExpression<PersonEntity>)
.Where("prop1s")
.Equal("value")
.And("tenant1")
.NotIn("tag1", "tag2", "tag3")
.And("prop2")
.Equal("newValue");

var queryStr = builder.Build();

queryStr.Trim()
.Should()
.Be("prop1s eq 'value' and (tenant1 ne 'tag1' and tenant1 ne 'tag2' and tenant1 ne 'tag3') and prop2 eq 'newValue'");
}

[TestMethod]
public void Should_Use_NotIn_Filter_At_Start_Of_ExpressionFilter_Prop2()
{
var builder = new TableStorageQueryBuilder<PersonEntity>(new TagFilterExpression<PersonEntity>());

(builder.Query as TagFilterExpression<PersonEntity>)
.Where("tenant1")
.NotIn("tag1", "tag2", "tag3")
.And("prop2")
.Equal("newValue");

var queryStr = builder.Build();

queryStr.Trim()
.Should()
.Be("(tenant1 ne 'tag1' and tenant1 ne 'tag2' and tenant1 ne 'tag3') and prop2 eq 'newValue'");
}
}
}

0 comments on commit a60c18d

Please sign in to comment.