in sdk/monitor/Azure.Monitor.OpenTelemetry.LiveMetrics/src/Internals/Filtering/Filter.cs [459:780]
private Expression ProduceComparatorExpressionForSingleFieldCondition(Expression fieldExpression, Type fieldType, bool isFieldTypeNullable = false)
{
// this must determine an appropriate runtime comparison given the field type, the predicate, and the comparand
TypeCode fieldTypeCode = Type.GetTypeCode(fieldType);
switch (fieldTypeCode)
{
case TypeCode.Boolean:
{
ThrowOnInvalidFilter(fieldType, !comparandBoolean.HasValue);
switch (predicate)
{
case Predicate.Equal:
// fieldValue == this.comparandBoolean.Value;
return Expression.Equal(fieldExpression, Expression.Constant(comparandBoolean.Value, isFieldTypeNullable ? typeof(bool?) : typeof(bool)));
case Predicate.NotEqual:
// fieldValue != this.comparandBoolean.Value;
return Expression.NotEqual(fieldExpression, Expression.Constant(comparandBoolean.Value, isFieldTypeNullable ? typeof(bool?) : typeof(bool)));
default:
ThrowOnInvalidFilter(fieldType);
break;
}
}
break;
case TypeCode.SByte:
case TypeCode.Int16:
case TypeCode.Int32:
case TypeCode.Int64:
case TypeCode.Byte:
case TypeCode.UInt16:
case TypeCode.UInt32:
case TypeCode.UInt64:
case TypeCode.Single:
case TypeCode.Double:
{
if (fieldType.GetTypeInfo().IsEnum)
{
// this is actually an Enum
object enumValue = null;
try
{
enumValue = Enum.Parse(fieldType, comparand, true);
}
catch (System.Exception)
{
// we must throw unless this.predicate is either Contains or DoesNotContain, in which case it's ok
ThrowOnInvalidFilter(fieldType, predicate != Predicate.Contains && predicate != Predicate.DoesNotContain);
}
Type enumUnderlyingType = fieldType.GetTypeInfo().GetEnumUnderlyingType();
// This block matches the case statements just above.
static Type GetNullableType(Type inputType) => inputType switch
{
_ when inputType == typeof(sbyte) => typeof(sbyte?),
_ when inputType == typeof(short) => typeof(short?),
_ when inputType == typeof(int) => typeof(int?),
_ when inputType == typeof(long) => typeof(long?),
_ when inputType == typeof(byte) => typeof(byte?),
_ when inputType == typeof(ushort) => typeof(ushort?),
_ when inputType == typeof(uint) => typeof(uint?),
_ when inputType == typeof(ulong) => typeof(ulong?),
_ when inputType == typeof(float) => typeof(float?),
_ when inputType == typeof(double) => typeof(double?),
_ => throw new ArgumentException($"Cannot create a nullable type for {inputType.FullName}."),
};
switch (predicate)
{
case Predicate.Equal:
// fieldValue == enumValue
if (isFieldTypeNullable)
{
// For nullable enums, we need to use a different approach
// First, check if the field is null, then check for equality if not null
// (fieldExpression == null ? false : fieldExpression.Value == enumValue)
return Expression.Condition(
Expression.Equal(fieldExpression, Expression.Constant(null, fieldExpression.Type)),
Expression.Constant(false),
Expression.Equal(
Expression.Convert(fieldExpression, fieldType),
Expression.Constant(enumValue, fieldType)));
}
else
{
return Expression.Equal(fieldExpression, Expression.Constant(enumValue, fieldType));
}
case Predicate.NotEqual:
// fieldValue != enumValue
if (isFieldTypeNullable)
{
// For nullable enums: (fieldExpression == null ? true : fieldExpression.Value != enumValue)
return Expression.Condition(
Expression.Equal(fieldExpression, Expression.Constant(null, fieldExpression.Type)),
Expression.Constant(true),
Expression.NotEqual(
Expression.Convert(fieldExpression, fieldType),
Expression.Constant(enumValue, fieldType)));
}
else
{
return Expression.NotEqual(fieldExpression, Expression.Constant(enumValue, fieldType));
}
case Predicate.LessThan:
// (int)fieldValue < (int)enumValue
// (int?)fieldValue < (int?)enumValue
Type underlyingType = isFieldTypeNullable ? GetNullableType(enumUnderlyingType) : enumUnderlyingType;
return Expression.LessThan(
Expression.Convert(fieldExpression, underlyingType),
Expression.Convert(Expression.Constant(enumValue, fieldType), underlyingType));
case Predicate.GreaterThan:
// (int)fieldValue > (int)enumValue
// (int?)fieldValue > (int?)enumValue
underlyingType = isFieldTypeNullable ? GetNullableType(enumUnderlyingType) : enumUnderlyingType;
return Expression.GreaterThan(
Expression.Convert(fieldExpression, underlyingType),
Expression.Convert(Expression.Constant(enumValue, fieldType), underlyingType));
case Predicate.LessThanOrEqual:
// (int)fieldValue <= (int)enumValue
// (int?)fieldValue <= (int?)enumValue
underlyingType = isFieldTypeNullable ? GetNullableType(enumUnderlyingType) : enumUnderlyingType;
return Expression.LessThanOrEqual(
Expression.Convert(fieldExpression, underlyingType),
Expression.Convert(Expression.Constant(enumValue, fieldType), underlyingType));
case Predicate.GreaterThanOrEqual:
// (int)fieldValue >= (int)enumValue
// (int?)fieldValue >= (int?)enumValue
underlyingType = isFieldTypeNullable ? GetNullableType(enumUnderlyingType) : enumUnderlyingType;
return Expression.GreaterThanOrEqual(
Expression.Convert(fieldExpression, underlyingType),
Expression.Convert(Expression.Constant(enumValue, fieldType), underlyingType));
case Predicate.Contains:
// fieldValue.ToString(CultureInfo.InvariantCulture).IndexOf(this.comparand, StringComparison.OrdinalIgnoreCase) != -1
Expression toStringCall = Expression.Call(fieldExpression, isFieldTypeNullable ? ValueTypeToStringMethodInfo : ObjectToStringMethodInfo);
Expression indexOfCall = Expression.Call(toStringCall, StringIndexOfMethodInfo, Expression.Constant(comparand), Expression.Constant(StringComparison.OrdinalIgnoreCase));
return Expression.NotEqual(indexOfCall, Expression.Constant(-1));
case Predicate.DoesNotContain:
// fieldValue.ToString(CultureInfo.InvariantCulture).IndexOf(this.comparand, StringComparison.OrdinalIgnoreCase) == -1
toStringCall = Expression.Call(fieldExpression, isFieldTypeNullable ? ValueTypeToStringMethodInfo : ObjectToStringMethodInfo);
indexOfCall = Expression.Call(toStringCall, StringIndexOfMethodInfo, Expression.Constant(comparand), Expression.Constant(StringComparison.OrdinalIgnoreCase));
return Expression.Equal(indexOfCall, Expression.Constant(-1));
default:
ThrowOnInvalidFilter(fieldType);
break;
}
}
else
{
// this is a regular numerical type
// in order for the expression to compile, we must cast to double unless it's already double
// we're using double as the lowest common denominator for all numerical types
Expression fieldConvertedExpression = fieldTypeCode == TypeCode.Double
? fieldExpression
: Expression.ConvertChecked(fieldExpression, isFieldTypeNullable ? typeof(double?) : typeof(double));
switch (predicate)
{
case Predicate.Equal:
ThrowOnInvalidFilter(fieldType, !comparandDouble.HasValue);
return Expression.Equal(
fieldConvertedExpression,
Expression.Constant(comparandDouble.Value, isFieldTypeNullable ? typeof(double?) : typeof(double)));
case Predicate.NotEqual:
ThrowOnInvalidFilter(fieldType, !comparandDouble.HasValue);
return Expression.NotEqual(
fieldConvertedExpression,
Expression.Constant(comparandDouble.Value, isFieldTypeNullable ? typeof(double?) : typeof(double)));
case Predicate.LessThan:
ThrowOnInvalidFilter(fieldType, !comparandDouble.HasValue);
return Expression.LessThan(
fieldConvertedExpression,
Expression.Constant(comparandDouble.Value, isFieldTypeNullable ? typeof(double?) : typeof(double)));
case Predicate.GreaterThan:
ThrowOnInvalidFilter(fieldType, !comparandDouble.HasValue);
return Expression.GreaterThan(
fieldConvertedExpression,
Expression.Constant(comparandDouble.Value, isFieldTypeNullable ? typeof(double?) : typeof(double)));
case Predicate.LessThanOrEqual:
ThrowOnInvalidFilter(fieldType, !comparandDouble.HasValue);
return Expression.LessThanOrEqual(
fieldConvertedExpression,
Expression.Constant(comparandDouble.Value, isFieldTypeNullable ? typeof(double?) : typeof(double)));
case Predicate.GreaterThanOrEqual:
ThrowOnInvalidFilter(fieldType, !comparandDouble.HasValue);
return Expression.GreaterThanOrEqual(
fieldConvertedExpression,
Expression.Constant(comparandDouble.Value, isFieldTypeNullable ? typeof(double?) : typeof(double)));
case Predicate.Contains:
// fieldValue.ToString(CultureInfo.InvariantCulture).IndexOf(this.comparand, StringComparison.OrdinalIgnoreCase) != -1
Expression toStringCall = isFieldTypeNullable
? Expression.Call(fieldConvertedExpression, NullableDoubleToStringMethodInfo)
: Expression.Call(fieldConvertedExpression, DoubleToStringMethodInfo, Expression.Constant(CultureInfo.InvariantCulture));
Expression indexOfCall = Expression.Call(toStringCall, StringIndexOfMethodInfo, Expression.Constant(comparand), Expression.Constant(StringComparison.OrdinalIgnoreCase));
return Expression.NotEqual(indexOfCall, Expression.Constant(-1));
case Predicate.DoesNotContain:
// fieldValue.ToString(CultureInfo.InvariantCulture).IndexOf(this.comparand, StringComparison.OrdinalIgnoreCase) == -1
toStringCall = isFieldTypeNullable
? Expression.Call(fieldConvertedExpression, NullableDoubleToStringMethodInfo)
: Expression.Call(fieldConvertedExpression, DoubleToStringMethodInfo, Expression.Constant(CultureInfo.InvariantCulture));
indexOfCall = Expression.Call(toStringCall, StringIndexOfMethodInfo, Expression.Constant(comparand), Expression.Constant(StringComparison.OrdinalIgnoreCase));
return Expression.Equal(indexOfCall, Expression.Constant(-1));
default:
ThrowOnInvalidFilter(fieldType);
break;
}
}
}
break;
case TypeCode.String:
{
Expression fieldValueOrEmptyString = Expression.Condition(Expression.Equal(fieldExpression, Expression.Constant(null)), Expression.Constant(string.Empty), fieldExpression);
Expression indexOfCall = Expression.Call(fieldValueOrEmptyString, StringIndexOfMethodInfo, Expression.Constant(comparand), Expression.Constant(StringComparison.OrdinalIgnoreCase));
switch (predicate)
{
case Predicate.Equal:
// (fieldValue ?? string.Empty).Equals(this.comparand, StringComparison.OrdinalIgnoreCase)
return Expression.Call(fieldValueOrEmptyString, StringEqualsMethodInfo, Expression.Constant(comparand), Expression.Constant(StringComparison.OrdinalIgnoreCase));
case Predicate.NotEqual:
// !(fieldValue ?? string.Empty).Equals(this.comparand, StringComparison.OrdinalIgnoreCase)
return Expression.Not(Expression.Call(fieldValueOrEmptyString, StringEqualsMethodInfo, Expression.Constant(comparand), Expression.Constant(StringComparison.OrdinalIgnoreCase)));
case Predicate.LessThan:
case Predicate.GreaterThan:
case Predicate.LessThanOrEqual:
case Predicate.GreaterThanOrEqual:
// double.TryParse(fieldValue, out temp) && temp {<, <=, >, >=} comparandDouble
ThrowOnInvalidFilter(fieldType, !comparandDouble.HasValue);
return CreateStringToDoubleComparisonBlock(fieldExpression, predicate);
case Predicate.Contains:
// fieldValue => (fieldValue ?? string.Empty).IndexOf(this.comparand, StringComparison.OrdinalIgnoreCase) != -1;
return Expression.NotEqual(indexOfCall, Expression.Constant(-1));
case Predicate.DoesNotContain:
// fieldValue => (fieldValue ?? string.Empty).IndexOf(this.comparand, StringComparison.OrdinalIgnoreCase) == -1;
return Expression.Equal(indexOfCall, Expression.Constant(-1));
default:
ThrowOnInvalidFilter(fieldType);
break;
}
}
break;
default:
Type nullableUnderlyingType;
if (fieldType == typeof(TimeSpan))
{
ThrowOnInvalidFilter(fieldType, !comparandTimeSpan.HasValue);
if (fieldExpression.Type == typeof(string))
{
MethodInfo parseMethod = typeof(TimeSpan).GetMethod("Parse", new[] { typeof(string) });
fieldExpression = Expression.Call(parseMethod, fieldExpression);
}
switch (predicate)
{
case Predicate.Equal:
Func<TimeSpan, bool> comparator = fieldValue => fieldValue == comparandTimeSpan.Value;
return Expression.Call(Expression.Constant(comparator.Target), comparator.GetMethodInfo(), fieldExpression);
case Predicate.NotEqual:
comparator = fieldValue => fieldValue != comparandTimeSpan.Value;
return Expression.Call(Expression.Constant(comparator.Target), comparator.GetMethodInfo(), fieldExpression);
case Predicate.LessThan:
comparator = fieldValue => fieldValue < comparandTimeSpan.Value;
return Expression.Call(Expression.Constant(comparator.Target), comparator.GetMethodInfo(), fieldExpression);
case Predicate.GreaterThan:
comparator = fieldValue => fieldValue > comparandTimeSpan.Value;
return Expression.Call(Expression.Constant(comparator.Target), comparator.GetMethodInfo(), fieldExpression);
case Predicate.LessThanOrEqual:
comparator = fieldValue => fieldValue <= comparandTimeSpan.Value;
return Expression.Call(Expression.Constant(comparator.Target), comparator.GetMethodInfo(), fieldExpression);
case Predicate.GreaterThanOrEqual:
comparator = fieldValue => fieldValue >= comparandTimeSpan.Value;
return Expression.Call(Expression.Constant(comparator.Target), comparator.GetMethodInfo(), fieldExpression);
default:
ThrowOnInvalidFilter(fieldType);
break;
}
}
else if (fieldType == typeof(Uri))
{
Expression toStringCall = Expression.Call(fieldExpression, UriToStringMethodInfo);
Expression fieldValueOrEmptyString = Expression.Condition(Expression.Equal(fieldExpression, Expression.Constant(null)), Expression.Constant(string.Empty), toStringCall);
Expression indexOfCall = Expression.Call(fieldValueOrEmptyString, StringIndexOfMethodInfo, Expression.Constant(comparand), Expression.Constant(StringComparison.OrdinalIgnoreCase));
switch (predicate)
{
case Predicate.Equal:
// (fieldValue?.ToString() ?? string.Empty).Equals(this.comparand, StringComparison.OrdinalIgnoreCase)
return Expression.Call(fieldValueOrEmptyString, StringEqualsMethodInfo, Expression.Constant(comparand), Expression.Constant(StringComparison.OrdinalIgnoreCase));
case Predicate.NotEqual:
// !(fieldValue?.ToString() ?? string.Empty).Equals(this.comparand, StringComparison.OrdinalIgnoreCase)
return Expression.Not(Expression.Call(fieldValueOrEmptyString, StringEqualsMethodInfo, Expression.Constant(comparand), Expression.Constant(StringComparison.OrdinalIgnoreCase)));
case Predicate.Contains:
// fieldValue => (fieldValue?.ToString() ?? string.Empty).IndexOf(this.comparand, StringComparison.OrdinalIgnoreCase) != -1;
return Expression.NotEqual(indexOfCall, Expression.Constant(-1));
case Predicate.DoesNotContain:
// fieldValue => (fieldValue?.ToString() ?? string.Empty).IndexOf(this.comparand, StringComparison.OrdinalIgnoreCase) == -1;
return Expression.Equal(indexOfCall, Expression.Constant(-1));
default:
ThrowOnInvalidFilter(fieldType);
break;
}
}
else if ((nullableUnderlyingType = Nullable.GetUnderlyingType(fieldType)) != null)
{
// make a recursive call for the underlying type
return ProduceComparatorExpressionForSingleFieldCondition(fieldExpression, nullableUnderlyingType, true);
}
else
{
ThrowOnInvalidFilter(fieldType);
}
break;
}
return null;
}