From dbed12ddc930e21bb4b97ba47bca16eb52e93904 Mon Sep 17 00:00:00 2001 From: Sean McArdle Date: Mon, 13 Mar 2023 12:53:26 -0700 Subject: [PATCH] Added solution file and updated filter extensions --- .idea/.idea.Ruler/.idea/.gitignore | 13 +++ .idea/.idea.Ruler/.idea/indexLayout.xml | 8 ++ .idea/.idea.Ruler/.idea/vcs.xml | 6 ++ Ruler.sln | 28 ++++++ Ruler/Ruler.cs | 149 ++++++++++++++++++++------------ Ruler/Ruler.csproj | 2 +- RulerDev/RulerDev.csproj | 10 +-- global.json | 7 ++ 8 files changed, 164 insertions(+), 59 deletions(-) create mode 100644 .idea/.idea.Ruler/.idea/.gitignore create mode 100644 .idea/.idea.Ruler/.idea/indexLayout.xml create mode 100644 .idea/.idea.Ruler/.idea/vcs.xml create mode 100644 Ruler.sln create mode 100644 global.json diff --git a/.idea/.idea.Ruler/.idea/.gitignore b/.idea/.idea.Ruler/.idea/.gitignore new file mode 100644 index 0000000..de3eb63 --- /dev/null +++ b/.idea/.idea.Ruler/.idea/.gitignore @@ -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 diff --git a/.idea/.idea.Ruler/.idea/indexLayout.xml b/.idea/.idea.Ruler/.idea/indexLayout.xml new file mode 100644 index 0000000..7b08163 --- /dev/null +++ b/.idea/.idea.Ruler/.idea/indexLayout.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/.idea.Ruler/.idea/vcs.xml b/.idea/.idea.Ruler/.idea/vcs.xml new file mode 100644 index 0000000..35eb1dd --- /dev/null +++ b/.idea/.idea.Ruler/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/Ruler.sln b/Ruler.sln new file mode 100644 index 0000000..b2d4c04 --- /dev/null +++ b/Ruler.sln @@ -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 diff --git a/Ruler/Ruler.cs b/Ruler/Ruler.cs index e0e6a6a..06af9ef 100644 --- a/Ruler/Ruler.cs +++ b/Ruler/Ruler.cs @@ -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> GetFilterForType(string property, string value) - { - var parameter = Expression.Parameter(typeof(T), "x"); - var opLeft = Expression.Property(parameter, property); + public static Expression> AddFilterToStringProperty( + Expression> 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>(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> CombineAnd(IEnumerable>> 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>( + filterExpression, + expression.Parameters); + } + + // Dynamically build an expression suitable for filtering in a Where clause + public static Expression> GetFilterForType(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>(opLeft, parameter); + + if (value.StartsWith("*") && value.EndsWith("*")) { + return AddFilterToStringProperty(strParam, value.Trim('*'), "Contains"); + } else if (value.StartsWith("*")) { + return AddFilterToStringProperty(strParam, value.TrimStart('*'), "EndsWith"); + } else if (value.EndsWith("*")) { + return AddFilterToStringProperty(strParam, value.TrimEnd('*'), "StartsWith"); + } else { + return Expression.Lambda>(comparison, parameter); + } + } + + return Expression.Lambda>(comparison, parameter); + } + + // Combine a list of expressions inclusively + public static Expression>? CombineAnd(IEnumerable>> 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> CombineOr(IEnumerable>> predicates) - { - if (predicates.Count() == 0) return null; + // Combine a list of expressions inclusively + public static Expression>? CombineOr(IEnumerable>> 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> CombinePredicates(IEnumerable>> predicates, FilterPolicyExtensions.RuleOperator op) - { - if (predicates.Count() == 0) return null; + // Combine a list of expressions inclusively + public static Expression>? CombinePredicates(IEnumerable>> 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> GetFilterExpression(this FilterPolicy policy) - { - var predicates = new List>>(); - foreach (var constraints in policy.scope) - { - predicates.Add(GetFilterForType(constraints.Item1, constraints.Item2)); - } - - return CombinePredicates(predicates, policy.ruleOperator); - } + public static Expression>? GetFilterExpression(this FilterPolicy policy) + { + var predicates = new List>>(); + foreach (var constraints in policy.scope) + { + predicates.Add(GetFilterForType(constraints.Item1, constraints.Item2)); + } + + return CombinePredicates(predicates, policy.ruleOperator); + } } \ No newline at end of file diff --git a/Ruler/Ruler.csproj b/Ruler/Ruler.csproj index 4658cbf..bafd05b 100644 --- a/Ruler/Ruler.csproj +++ b/Ruler/Ruler.csproj @@ -1,7 +1,7 @@ - net7.0 + net6.0 enable enable diff --git a/RulerDev/RulerDev.csproj b/RulerDev/RulerDev.csproj index 3a1aeab..43f7d06 100644 --- a/RulerDev/RulerDev.csproj +++ b/RulerDev/RulerDev.csproj @@ -1,16 +1,16 @@ - - + + - - + + Exe - net7.0 + net6.0 enable enable diff --git a/global.json b/global.json new file mode 100644 index 0000000..87aef9f --- /dev/null +++ b/global.json @@ -0,0 +1,7 @@ +{ + "sdk": { + "version": "6.0.0", + "rollForward": "latestMajor", + "allowPrerelease": false + } +} \ No newline at end of file