Fixed intent of negative match. The Not expression was a bitwise operation rather than logical operation. Whoops.

pull/5/head
Sean McArde 2023-07-24 10:49:36 -07:00 committed by Sean McArdle
parent 8081903698
commit 9478aa9bfc
4 changed files with 41 additions and 19 deletions

View File

@ -8,7 +8,7 @@
<UserSecretsId>63a98c68-03bd-4069-b6b9-e0978081c430</UserSecretsId>
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
<Title>McRule - Rule based expression generator</Title>
<Version>0.1.1</Version>
<Version>0.1.2</Version>
<Company>Sean McArdle</Company>
<Description>Library for generating expression trees from simple policy rules.</Description>
<Copyright>2023 Sean McArdle</Copyright>

View File

@ -19,8 +19,8 @@ public static class PredicateExpressionPolicyExtensions {
/// <summary>
/// Builds expressions using string member functions StartsWith, EndsWith or Contains as the comparator.
/// </summary>
public static Expression<Func<T, bool>> AddStringPropertyExpression<T>(
Expression<Func<T, string>> expression, string filter, string filterType, bool ignoreCase=false)
public static Expression<Func<T, bool>> AddStringPropertyExpression<T>(
Expression<Func<T, string>> lambda, string filter, string filterType, bool ignoreCase = false)
{
#if DEBUG
@ -31,7 +31,7 @@ public static class PredicateExpressionPolicyExtensions {
#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));
var notNull = Expression.NotEqual(lambda.Body, Expression.Constant(null));
// Setup calls to: StartsWith, EndsWith, Contains, or Equals,
// conditionally using character case neutral comparision.
@ -42,13 +42,13 @@ public static class PredicateExpressionPolicyExtensions {
}
MethodInfo methodInfo = typeof(string).GetMethod(filterType, new[] { typeof(string), typeof(StringComparison) });
var strPredicate = Expression.Call(expression.Body, methodInfo, expressionArgs);
var strPredicate = Expression.Call(lambda.Body, methodInfo, expressionArgs);
var filterExpression = Expression.AndAlso(notNull, strPredicate);
Expression filterExpression = Expression.AndAlso(notNull, strPredicate);
return Expression.Lambda<Func<T, bool>>(
filterExpression,
expression.Parameters);
lambda.Parameters);
}
/// <summary>
@ -69,8 +69,14 @@ public static class PredicateExpressionPolicyExtensions {
/// <typeparam name="T"></typeparam>
/// <param name="operand"></param>
/// <returns></returns>
public static Expression<Func<T, bool>> Negate<T>(Expression operand) {
return Expression.Lambda<Func<T, bool>>(Expression.Not(operand));
public static Expression<Func<T, bool>> Negate<T>(Expression<Func<T, bool>> lambda) {
var body = lambda.Body;
var parameters = lambda.Parameters;
var negated = Expression.IsFalse(body);
return Expression.Lambda<Func<T, bool>>(
negated,
parameters);
}
/// <summary>

View File

@ -6,15 +6,16 @@ Predicates are built using simple syntax to select comparison operators and meth
That is the policy specifies the type by name, property to match against and the value the property must have.
A simple equality comparison is used by default but operators can be prefixed to a policy operand for customized behavior, as shown below.
| Property Type | Operator | Comparison Description |
|---------------|----------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| string | * | Astrisk can appear at the beginning, end or both denoting: StartsWith, EndsWith or Contains respectively. |
| string | ~ | Denotes case-insensitive comparison when used in .net, things that translate the resulting expression tree may not respect this. EF core for instance won't bind a Contains with case insensitive comparison from the string methods at all, results in a runtime error. Note: when used with wildcard, tilde operator must be first: '~*foo'. |
| IComparable | > | Greater-than comparison. |
| IComparable | >= | Greater-than or equal to comparison. |
| IComparable | < | Less-than comparison. |
| IComparable | <= | Less-than or equal to comparison. |
| IComparable | <>, !=, ! | Not-equal to comparison |
| Property Type | Operator | Comparison Description |
|---------------|-----------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| string | * | Astrisk can appear at the beginning, end or both denoting: StartsWith, EndsWith or Contains respectively. |
| string | ~ | Denotes case-insensitive comparison when used in .net, things that translate the resulting expression tree may not respect this. EF core for instance won't bind a Contains with case insensitive comparison from the string methods at all, results in a runtime error. Note: when used with wildcard, tilde operator must be first: '~*foo'. |
| string | ! | Negative expression. Must prefix all other operators. |
| IComparable | > | Greater-than comparison. |
| IComparable | >= | Greater-than or equal to comparison. |
| IComparable | < | Less-than comparison. |
| IComparable | <= | Less-than or equal to comparison. |
| IComparable | <>, !=, ! | Not-equal to comparison. |
> Note: the IComparable interface is mostly used for numerical types but custom types with comparison providers may work at runtime.

View File

@ -80,9 +80,24 @@ var combined = PredicateExpressionPolicyExtensions.CombineAnd(policies);
users.Where(dynamicFilterBrian).Dump("All *ian's");
(from u in users.Where(combined)
select new { u.first, u.last, u.agency}).Dump("All Brian's from DAS");
select new { u.first, u.last, u.agency}).Dump("All ian's from DAS");
var thePolicy = new ExpressionPolicy {
Name = "All DAS *ians case insensitive",
Properties = new string[] { }, // Can't do anything with this yet
Rules = new List<ExpressionRule>
{
("Thing", "Name", "!Sean").ToFilterRule(),
}
};
var filter = thePolicy.GetExpression<Thing>();
filter.Dump();
new[] { new Thing("Sean", "Confused"), new Thing("Tim", "Enchantor") }.Where(filter.Compile()).Dump();
public record Thing(string Name, string Kind);
record User(string first, string last, string workPhone, string homePhone, string workAddress, string agency, string[] tags = null);