/// <summary> /// Applies the required conversion process to return a value of an expected type from an expression /// </summary> /// <param name="expr">The expression that is generating a value</param> /// <param name="type">The type of value required from an expression</param> /// <returns>An expression that converts the generated value to the required type</returns> public static Expression Convert(Expression expr, Type type) { // Simple case - expression already generates the required type if (expr.Type == type) { return(expr); } // If the generated type directly implements the required interface if (type.IsInterface && expr.Type.GetInterfaces().Contains(type)) { return(expr); } // Box value types if (expr.Type.IsValueType && type == typeof(object)) { return(Expression.Convert(expr, type)); } // WIP: Implicit type conversions to match SQL if (type == typeof(bool?)) { // https://docs.microsoft.com/en-us/sql/t-sql/data-types/bit-transact-sql if (expr.Type == typeof(string)) { return(Expr.Call(() => StringToBool(Arg <string>()), expr)); } if (expr.Type == typeof(int)) { expr = Expression.Convert(expr, typeof(int?)); } if (expr.Type == typeof(int?)) { return(Expr.Call(() => IntToBool(Arg <int?>()), expr)); } } // In-built conversions between value types if (expr.Type.IsValueType && type.IsValueType) { return(Expression.Convert(expr, type)); } // Parse string literals to DateTime values if (expr.Type == typeof(string) && type == typeof(DateTime?)) { return(Expr.Call(() => ParseDateTime(Arg <string>()), expr)); } // Convert integers to optionset values if (expr.Type == typeof(int) && type == typeof(OptionSetValue)) { return(Expr.Call(() => CreateOptionSetValue(Arg <int>()), expr)); } if (expr.Type == typeof(int?) && type == typeof(OptionSetValue)) { return(Expr.Call(() => CreateOptionSetValue(Arg <int?>()), expr)); } // Extract IDs from EntityReferences if (expr.Type == typeof(EntityReference) && type == typeof(Guid?)) { return(Expr.Call(() => GetEntityReferenceId(Arg <EntityReference>()), expr)); } // Check for compatible class types if (expr.Type.IsClass && type.IsClass) { var baseType = expr.Type.BaseType; while (baseType != null) { if (baseType == type) { return(expr); } baseType = baseType.BaseType; } } // Check for compatible array types if (expr.Type.IsArray && type.IsArray) { if (type.GetElementType().IsAssignableFrom(expr.Type.GetElementType())) { return(expr); } } throw new NotSupportedException($"Cannot convert from {expr.Type} to {type}"); }