Added solution file and updated filter extensions

pull/2/head
Sean McArde 2023-03-13 12:53:26 -07:00
parent f193376a6d
commit dbed12ddc9
8 changed files with 164 additions and 59 deletions

13
.idea/.idea.Ruler/.idea/.gitignore vendored Normal file
View File

@ -0,0 +1,13 @@
# Default ignored files
/shelf/
/workspace.xml
# Rider ignored files
/modules.xml
/.idea.Ruler.iml
/contentModel.xml
/projectSettingsUpdater.xml
# Editor-based HTTP Client requests
/httpRequests/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="UserContentModel">
<attachedFolders />
<explicitIncludes />
<explicitExcludes />
</component>
</project>

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="Git" />
</component>
</project>

28
Ruler.sln Normal file
View File

@ -0,0 +1,28 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.0.31903.59
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ruler", "Ruler\Ruler.csproj", "{E4B8E05A-80A5-4F0D-A3DA-BB6AA377153D}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RulerDev", "RulerDev\RulerDev.csproj", "{0E6DF52C-D171-4E84-8D6D-BB580DF35E51}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{E4B8E05A-80A5-4F0D-A3DA-BB6AA377153D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{E4B8E05A-80A5-4F0D-A3DA-BB6AA377153D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E4B8E05A-80A5-4F0D-A3DA-BB6AA377153D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E4B8E05A-80A5-4F0D-A3DA-BB6AA377153D}.Release|Any CPU.Build.0 = Release|Any CPU
{0E6DF52C-D171-4E84-8D6D-BB580DF35E51}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{0E6DF52C-D171-4E84-8D6D-BB580DF35E51}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0E6DF52C-D171-4E84-8D6D-BB580DF35E51}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0E6DF52C-D171-4E84-8D6D-BB580DF35E51}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobal

View File

@ -59,71 +59,114 @@ public static class PredicateBuilder
public static class FilterPolicyExtensions
{
public enum RuleOperator {
And,
Or
}
{
public enum RuleOperator {
And,
Or
}
// Dynamicall build an expression suitable for filtering in a Where clause
public static Expression<Func<T, bool>> GetFilterForType<T>(string property, string value)
{
var parameter = Expression.Parameter(typeof(T), "x");
var opLeft = Expression.Property(parameter, property);
public static Expression<Func<T, bool>> AddFilterToStringProperty<T>(
Expression<Func<T, string>> expression, string filter, string filterType)
{
var opRight = Expression.Constant(value);
var comparison = Expression.Equal(opLeft, opRight);
#if DEBUG
if (!(filterType == "StartsWith" || filterType == "EndsWith" || filterType == "Contains"))
{
throw new Exception($"filterType must equal StartsWith, EndsWith or Contains. Passed {filterType}");
}
return Expression.Lambda<Func<T, bool>>(comparison, parameter);
}
#endif
// Check that the property isn't null, otherwise we'd hit null object exceptions at runtime
var notNull = Expression.NotEqual(expression.Body,Expression.Constant(null));
// Setup calls to EtartsWith, EndsWith, or Contains
// TODO expressionArgs was used to pass multiple values for case insensitive compare, wasn't
// mapping the method correctly when used with EF so need to revisit that
var expressionArgs = new Expression[] {Expression.Constant(filter)};
var strPredicate = Expression.Call(expression.Body, filterType, null, expressionArgs);
// Combine a list of expressions inclusively
public static Expression<Func<T, bool>> CombineAnd<T>(IEnumerable<Expression<Func<T, bool>>> predicates)
{
if (predicates.Count() == 0) return null;
var filterExpression = Expression.AndAlso(notNull, strPredicate);
var final = predicates.First();
foreach (var next in predicates.Skip(1))
final = PredicateBuilder.And(final, next);
return final;
}
return Expression.Lambda<Func<T, bool>>(
filterExpression,
expression.Parameters);
}
// Dynamically build an expression suitable for filtering in a Where clause
public static Expression<Func<T, bool>> GetFilterForType<T>(string property, string value)
{
var parameter = Expression.Parameter(typeof(T), "x");
var opLeft = Expression.Property(parameter, property);
var opRight = Expression.Constant(value);
var comparison = Expression.Equal(opLeft, opRight);
// For string comparisons using wildcards, trim the wildcard characters and pass to the comparison method
if (opLeft.Type == typeof(string)) {
// Grab the object property for use in the inner expression body
var strParam = Expression.Lambda<Func<T,string>>(opLeft, parameter);
if (value.StartsWith("*") && value.EndsWith("*")) {
return AddFilterToStringProperty<T>(strParam, value.Trim('*'), "Contains");
} else if (value.StartsWith("*")) {
return AddFilterToStringProperty<T>(strParam, value.TrimStart('*'), "EndsWith");
} else if (value.EndsWith("*")) {
return AddFilterToStringProperty<T>(strParam, value.TrimEnd('*'), "StartsWith");
} else {
return Expression.Lambda<Func<T, bool>>(comparison, parameter);
}
}
return Expression.Lambda<Func<T, bool>>(comparison, parameter);
}
// Combine a list of expressions inclusively
public static Expression<Func<T, bool>>? CombineAnd<T>(IEnumerable<Expression<Func<T, bool>>> predicates)
{
if (predicates.Count() == 0) return null;
var final = predicates.First();
foreach (var next in predicates.Skip(1))
final = PredicateBuilder.And(final, next);
return final;
}
// Combine a list of expressions inclusively
public static Expression<Func<T, bool>> CombineOr<T>(IEnumerable<Expression<Func<T, bool>>> predicates)
{
if (predicates.Count() == 0) return null;
// Combine a list of expressions inclusively
public static Expression<Func<T, bool>>? CombineOr<T>(IEnumerable<Expression<Func<T, bool>>> predicates)
{
if (predicates.Count() == 0) return null;
var final = predicates.First();
foreach (var next in predicates.Skip(1))
final = PredicateBuilder.Or(final, next);
var final = predicates.First();
foreach (var next in predicates.Skip(1))
final = PredicateBuilder.Or(final, next);
return final;
}
return final;
}
// Combine a list of expressions inclusively
public static Expression<Func<T, bool>> CombinePredicates<T>(IEnumerable<Expression<Func<T, bool>>> predicates, FilterPolicyExtensions.RuleOperator op)
{
if (predicates.Count() == 0) return null;
// Combine a list of expressions inclusively
public static Expression<Func<T, bool>>? CombinePredicates<T>(IEnumerable<Expression<Func<T, bool>>> predicates,
FilterPolicyExtensions.RuleOperator op)
{
if (predicates.Count() == 0) return null;
if (op == RuleOperator.And)
{
return CombineAnd(predicates);
}
return CombineOr(predicates);
}
if (op == RuleOperator.And)
{
return CombineAnd(predicates);
}
return CombineOr(predicates);
}
public static Expression<Func<T, bool>> GetFilterExpression<T>(this FilterPolicy policy)
{
var predicates = new List<Expression<Func<T, bool>>>();
foreach (var constraints in policy.scope)
{
predicates.Add(GetFilterForType<T>(constraints.Item1, constraints.Item2));
}
return CombinePredicates<T>(predicates, policy.ruleOperator);
}
public static Expression<Func<T, bool>>? GetFilterExpression<T>(this FilterPolicy policy)
{
var predicates = new List<Expression<Func<T, bool>>>();
foreach (var constraints in policy.scope)
{
predicates.Add(GetFilterForType<T>(constraints.Item1, constraints.Item2));
}
return CombinePredicates<T>(predicates, policy.ruleOperator);
}
}

View File

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>

View File

@ -1,16 +1,16 @@
<Project Sdk="Microsoft.NET.Sdk">
<ItemGroup>
<ProjectReference Include="..\Ruler\Ruler.csproj" />
<ItemGroup>
<ProjectReference Include="..\Ruler\Ruler.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
</ItemGroup>
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net7.0</TargetFramework>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>

7
global.json Normal file
View File

@ -0,0 +1,7 @@
{
"sdk": {
"version": "6.0.0",
"rollForward": "latestMajor",
"allowPrerelease": false
}
}