Added negative predicate and negative string matches.

pull/5/head
Sean McArde 2023-07-24 09:42:10 -07:00 committed by Sean McArdle
parent 45f14c36c6
commit 8081903698
2 changed files with 42 additions and 22 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.0</Version>
<Version>0.1.1</Version>
<Company>Sean McArdle</Company>
<Description>Library for generating expression trees from simple policy rules.</Description>
<Copyright>2023 Sean McArdle</Copyright>

View File

@ -63,6 +63,16 @@ public static class PredicateExpressionPolicyExtensions {
return Expression.AndAlso(notNull, expression);
}
/// <summary>
/// Applies negative predicate to expression in a lambda.
/// </summary>
/// <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));
}
/// <summary>
/// Return a binary expression based on the given filter string. Default to a
/// standard Equals comparison.
@ -84,6 +94,7 @@ public static class PredicateExpressionPolicyExtensions {
typeof(Int16?), typeof(Int32?), typeof(Int64?),
typeof(UInt16?), typeof(UInt32?), typeof(UInt64?)};
/// <summary>
/// Dynamically build an expression suitable for filtering in a Where clause
/// </summary>
@ -112,12 +123,20 @@ public static class PredicateExpressionPolicyExtensions {
}
// For string comparisons using wildcards, trim the wildcard characters and pass to the comparison method
// For string comparisons using wildcards, trim the wildcard characters and pass to the comparison method
if (lType == typeof(string))
{
{
Expression<Func<T, bool>> result;
// Grab the object property for use in the inner expression body
var strParam = Expression.Lambda<Func<T, string>>(opLeft, parameter);
var strParam = Expression.Lambda<Func<T, string>>(opLeft, parameter);
// If a string match begins with !, we negate the result.
var negateResult = false;
if (value.StartsWith("!")) {
negateResult = true;
value = value.TrimStart('!');
}
// String comparisons which are prefixed with '~' will be evaluated ignoring case.
// Note: when expression trees are used outside .net, such as with EF to SQL Server,
@ -127,25 +146,26 @@ public static class PredicateExpressionPolicyExtensions {
if (value.StartsWith('~')) {
ignoreCase = true;
value = value.TrimStart('~');
}
}
if (value.StartsWith("*") && value.EndsWith("*"))
{
return AddStringPropertyExpression<T>(strParam, value.Trim('*'), "Contains", ignoreCase);
}
else if (value.StartsWith("*"))
{
return AddStringPropertyExpression<T>(strParam, value.TrimStart('*'), "EndsWith", ignoreCase);
}
else if (value.EndsWith("*"))
{
return AddStringPropertyExpression<T>(strParam, value.TrimEnd('*'), "StartsWith", ignoreCase);
}
else
{
return AddStringPropertyExpression<T>(strParam, value, "Equals", ignoreCase);
}
} else if (hasComparable == typeof(IComparable)) {
if (value.StartsWith("*") && value.EndsWith("*")) {
result = AddStringPropertyExpression<T>(strParam, value.Trim('*'), "Contains", ignoreCase);
} else if (value.StartsWith("*")) {
result = AddStringPropertyExpression<T>(strParam, value.TrimStart('*'), "EndsWith", ignoreCase);
} else if (value.EndsWith("*")) {
result = AddStringPropertyExpression<T>(strParam, value.TrimEnd('*'), "StartsWith", ignoreCase);
} else {
result = AddStringPropertyExpression<T>(strParam, value, "Equals", ignoreCase);
}
if (negateResult) {
result = Negate<T>(result);
}
return result;
}
else if (hasComparable == typeof(IComparable))
{
var operatorPrefix = Regex.Match(value.Trim(), @"^[!<>=]+");
var operand = (operatorPrefix.Success ? value.Replace(operatorPrefix.Value, "") : value).Trim();