Esempio n. 1
0
        /// <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}");
        }