File: System\Data\SqlClient\SqlTypesAssembly.cs
Project: ndp\fx\src\DataEntity\System.Data.Entity.csproj (System.Data.Entity)
//------------------------------------------------------------------------------
// <copyright file="SqlTypesAssembly.cs" company="Microsoft">
//      Copyright (c) Microsoft Corporation.  All rights reserved.
// </copyright>
//
// @owner  willa
// @backupOwner Microsoft
//------------------------------------------------------------------------------
 
using System.Collections.Generic;
using System.Data.Common.Utils;
using System.Data.Spatial;
using System.Data.SqlClient.Internal;
using System.Diagnostics;
using System.IO;
using System.Linq.Expressions;
using System.Reflection;
 
namespace System.Data.SqlClient
{
    internal static class Expressions
    {
        internal static Expression Null<TNullType>()
        {
            return Expression.Constant(null, typeof(TNullType));
        }
 
        internal static Expression Null(Type nullType)
        {
            return Expression.Constant(null, nullType);
        }
 
        internal static Expression<Func<TArg, TResult>> Lambda<TArg, TResult>(string argumentName, Func<ParameterExpression, Expression> createLambdaBodyGivenParameter)
        {
            ParameterExpression argParam = Expression.Parameter(typeof(TArg), argumentName);
            Expression lambdaBody = createLambdaBodyGivenParameter(argParam);
            return Expression.Lambda<Func<TArg, TResult>>(lambdaBody, argParam);
        }
 
        internal static Expression Call(this Expression exp, string methodName)
        {
            return Expression.Call(exp, methodName, Type.EmptyTypes);
        }
 
        internal static Expression ConvertTo(this Expression exp, Type convertToType)
        {
            return Expression.Convert(exp, convertToType);
        }
 
        internal static Expression ConvertTo<TConvertToType>(this Expression exp)
        {
            return Expression.Convert(exp, typeof(TConvertToType));
        }
 
        internal sealed class ConditionalExpressionBuilder
        {
            private readonly Expression condition;
            private readonly Expression ifTrueThen;
 
            internal ConditionalExpressionBuilder(Expression conditionExpression, Expression ifTrueExpression)
            {
                this.condition = conditionExpression;
                this.ifTrueThen = ifTrueExpression;
            }
 
            internal Expression Else(Expression resultIfFalse)
            {
                return Expression.Condition(this.condition, ifTrueThen, resultIfFalse);
            }
        }
 
        internal static ConditionalExpressionBuilder IfTrueThen(this Expression conditionExp, Expression resultIfTrue)
        {
            return new ConditionalExpressionBuilder(conditionExp, resultIfTrue);
        }
 
        internal static Expression Property<TPropertyType>(this Expression exp, string propertyName)
        {
            PropertyInfo prop = exp.Type.GetProperty(propertyName, BindingFlags.Instance | BindingFlags.Public);
            Debug.Assert(prop != null, "Type '" + exp.Type.FullName + "' does not declare a public instance property with the name '" + propertyName + "'");
 
            return Expression.Property(exp, prop);
        }
    }
 
    /// <summary>
    /// SqlTypesAssembly allows for late binding to the capabilities of a specific version of the Microsoft.SqlServer.Types assembly
    /// </summary>
    internal sealed class SqlTypesAssembly
    {
        private static readonly System.Collections.ObjectModel.ReadOnlyCollection<string> preferredSqlTypesAssemblies = new List<string>()
            {
                "Microsoft.SqlServer.Types, Version=11.0.0.0, Culture=neutral, PublicKeyToken=89845dcd8080cc91",
                "Microsoft.SqlServer.Types, Version=10.0.0.0, Culture=neutral, PublicKeyToken=89845dcd8080cc91",
            }.AsReadOnly();
 
        private static SqlTypesAssembly BindToLatest()
        {
            Assembly sqlTypesAssembly = null;
            foreach (string assemblyFullName in preferredSqlTypesAssemblies)
            {
                AssemblyName asmName = new AssemblyName(assemblyFullName);
                try
                {
                    sqlTypesAssembly = Assembly.Load(asmName);
                    break;
                }
                catch (FileNotFoundException)
                {
                }
                catch (FileLoadException)
                {
                } 
            }
 
            if (sqlTypesAssembly != null)
            {
                return new SqlTypesAssembly(sqlTypesAssembly);
            }
            return null;
        }
 
        internal static bool TryGetSqlTypesAssembly(Assembly assembly, out SqlTypesAssembly sqlAssembly)
        {
            if (IsKnownAssembly(assembly))
            {
                sqlAssembly = new SqlTypesAssembly(assembly);
                return true;
            }
            sqlAssembly = null;
            return false;
        }
 
        private static bool IsKnownAssembly(Assembly assembly)
        {
            foreach (string knownAssemblyFullName in preferredSqlTypesAssemblies)
            {
                if (EntityUtil.AssemblyNamesMatch(assembly.FullName, new AssemblyName(knownAssemblyFullName)))
                {
                    return true;
                }
            }
            return false;
        }
 
        private static Singleton<SqlTypesAssembly> latestVersion = new Singleton<SqlTypesAssembly>(BindToLatest);
 
        /// <summary>
        /// Returns the highest available version of the Microsoft.SqlServer.Types assembly that could be located using Assembly.Load; may return <c>null</c> if no version of the assembly could be found.
        /// </summary>
        internal static SqlTypesAssembly Latest { get { return latestVersion.Value; } }
 
        private SqlTypesAssembly(Assembly sqlSpatialAssembly)
        {
            // Retrieve SQL Server spatial types and static constructor methods
            Type sqlGeog = sqlSpatialAssembly.GetType("Microsoft.SqlServer.Types.SqlGeography", throwOnError: true);
            Type sqlGeom = sqlSpatialAssembly.GetType("Microsoft.SqlServer.Types.SqlGeometry", throwOnError: true);
 
            Debug.Assert(sqlGeog != null, "SqlGeography type was not properly retrieved?");
            Debug.Assert(sqlGeom != null, "SqlGeometry type was not properly retrieved?");
 
            this.SqlGeographyType = sqlGeog;
            this.sqlGeographyFromWKTString = CreateStaticConstructorDelegate<string>(sqlGeog, "STGeomFromText");
            this.sqlGeographyFromWKBByteArray = CreateStaticConstructorDelegate<byte[]>(sqlGeog, "STGeomFromWKB");
            this.sqlGeographyFromGMLReader = CreateStaticConstructorDelegate<System.Xml.XmlReader>(sqlGeog, "GeomFromGml");
 
            this.SqlGeometryType = sqlGeom;
            this.sqlGeometryFromWKTString = CreateStaticConstructorDelegate<string>(sqlGeom, "STGeomFromText");
            this.sqlGeometryFromWKBByteArray = CreateStaticConstructorDelegate<byte[]>(sqlGeom, "STGeomFromWKB");
            this.sqlGeometryFromGMLReader = CreateStaticConstructorDelegate<System.Xml.XmlReader>(sqlGeom, "GeomFromGml");
 
            // Retrieve SQL Server specific primitive types
            MethodInfo asTextMethod = this.SqlGeometryType.GetMethod("STAsText", BindingFlags.Public | BindingFlags.Instance, null, Type.EmptyTypes, null);
            this.SqlCharsType = asTextMethod.ReturnType;
            this.SqlStringType = this.SqlCharsType.Assembly.GetType("System.Data.SqlTypes.SqlString", throwOnError: true);
            this.SqlBooleanType = this.SqlCharsType.Assembly.GetType("System.Data.SqlTypes.SqlBoolean", throwOnError: true);
            this.SqlBytesType = this.SqlCharsType.Assembly.GetType("System.Data.SqlTypes.SqlBytes", throwOnError: true);
            this.SqlDoubleType = this.SqlCharsType.Assembly.GetType("System.Data.SqlTypes.SqlDouble", throwOnError: true);
            this.SqlInt32Type = this.SqlCharsType.Assembly.GetType("System.Data.SqlTypes.SqlInt32", throwOnError: true);
            this.SqlXmlType = this.SqlCharsType.Assembly.GetType("System.Data.SqlTypes.SqlXml", throwOnError: true);
 
            // Create type conversion delegates to SQL Server types
            this.sqlBytesFromByteArray = Expressions.Lambda<byte[], object>("binaryValue", bytesVal => BuildConvertToSqlBytes(bytesVal, this.SqlBytesType)).Compile();
            this.sqlStringFromString = Expressions.Lambda<string, object>("stringValue", stringVal => BuildConvertToSqlString(stringVal, this.SqlStringType)).Compile();
            this.sqlCharsFromString = Expressions.Lambda<string, object>("stringValue", stringVal => BuildConvertToSqlChars(stringVal, this.SqlCharsType)).Compile();
            this.sqlXmlFromXmlReader = Expressions.Lambda<System.Xml.XmlReader, object>("readerVaue", readerVal => BuildConvertToSqlXml(readerVal, this.SqlXmlType)).Compile();
            
            // Create type conversion delegates from SQL Server types; all arguments are typed as 'object' and require Expression.Convert before use of members of the SQL Server type.
 
            // Explicit cast from SqlBoolean to bool
            this.sqlBooleanToBoolean = Expressions.Lambda<object, bool>("sqlBooleanValue", sqlBoolVal => sqlBoolVal.ConvertTo(this.SqlBooleanType).ConvertTo<bool>()).Compile();
 
            // Explicit cast from SqlBoolean to bool? for non-Null values; otherwise null
            this.sqlBooleanToNullableBoolean = Expressions.Lambda<object, bool?>("sqlBooleanValue", sqlBoolVal =>
                sqlBoolVal.ConvertTo(this.SqlBooleanType).Property<bool>("IsNull")
                    .IfTrueThen(Expressions.Null<bool?>())
                    .Else(sqlBoolVal.ConvertTo(this.SqlBooleanType).ConvertTo<bool>().ConvertTo<bool?>())).Compile();
 
            // SqlBytes has instance byte[] property 'Value'
            this.sqlBytesToByteArray = Expressions.Lambda<object, byte[]>("sqlBytesValue", sqlBytesVal => sqlBytesVal.ConvertTo(this.SqlBytesType).Property<byte[]>("Value")).Compile();
 
            // SqlChars -> SqlString, SqlString has instance string property 'Value'
            this.sqlCharsToString = Expressions.Lambda<object, string>("sqlCharsValue", sqlCharsVal => sqlCharsVal.ConvertTo(this.SqlCharsType).Call("ToSqlString").Property<string>("Value")).Compile();
            
            // Explicit cast from SqlString to string
            this.sqlStringToString = Expressions.Lambda<object, string>("sqlStringValue", sqlStringVal => sqlStringVal.ConvertTo(this.SqlStringType).Property<string>("Value")).Compile();
 
            // Explicit cast from SqlDouble to double
            this.sqlDoubleToDouble = Expressions.Lambda<object, double>("sqlDoubleValue", sqlDoubleVal => sqlDoubleVal.ConvertTo(this.SqlDoubleType).ConvertTo<double>()).Compile();
 
            // Explicit cast from SqlDouble to double? for non-Null values; otherwise null
            this.sqlDoubleToNullableDouble = Expressions.Lambda<object, double?>("sqlDoubleValue", sqlDoubleVal =>
                sqlDoubleVal.ConvertTo(this.SqlDoubleType).Property<bool>("IsNull")
                    .IfTrueThen(Expressions.Null<double?>())
                    .Else(sqlDoubleVal.ConvertTo(this.SqlDoubleType).ConvertTo<double>().ConvertTo<double?>())).Compile();
 
            // Explicit cast from SqlInt32 to int
            this.sqlInt32ToInt = Expressions.Lambda<object, int>("sqlInt32Value", sqlInt32Val => sqlInt32Val.ConvertTo(this.SqlInt32Type).ConvertTo<int>()).Compile();
 
            // Explicit cast from SqlInt32 to int? for non-Null values; otherwise null
            this.sqlInt32ToNullableInt = Expressions.Lambda<object, int?>("sqlInt32Value", sqlInt32Val =>
                sqlInt32Val.ConvertTo(this.SqlInt32Type).Property<bool>("IsNull")
                    .IfTrueThen(Expressions.Null<int?>())
                    .Else(sqlInt32Val.ConvertTo(this.SqlInt32Type).ConvertTo<int>().ConvertTo<int?>())).Compile();
 
            // SqlXml has instance string property 'Value'
            this.sqlXmlToString = Expressions.Lambda<object, string>("sqlXmlValue", sqlXmlVal => sqlXmlVal.ConvertTo(this.SqlXmlType).Property<string>("Value")).Compile();
 
            this.isSqlGeographyNull = Expressions.Lambda<object, bool>("sqlGeographyValue", sqlGeographyValue => sqlGeographyValue.ConvertTo(this.SqlGeographyType).Property<bool>("IsNull")).Compile();
            this.isSqlGeometryNull = Expressions.Lambda<object, bool>("sqlGeometryValue", sqlGeometryValue => sqlGeometryValue.ConvertTo(this.SqlGeometryType).Property<bool>("IsNull")).Compile();
            
            this.geographyAsTextZMAsSqlChars = Expressions.Lambda<object, object>("sqlGeographyValue", sqlGeographyValue => sqlGeographyValue.ConvertTo(this.SqlGeographyType).Call("AsTextZM")).Compile();
            this.geometryAsTextZMAsSqlChars = Expressions.Lambda<object, object>("sqlGeometryValue", sqlGeometryValue => sqlGeometryValue.ConvertTo(this.SqlGeometryType).Call("AsTextZM")).Compile();
        }
 
        #region 'Public' API
 
        #region Primitive Types and Type Conversions
 
        internal Type SqlBooleanType { get; private set; }
        internal Type SqlBytesType { get; private set; }
        internal Type SqlCharsType { get; private set; }
        internal Type SqlStringType { get; private set; }
        internal Type SqlDoubleType { get; private set; }
        internal Type SqlInt32Type { get; private set; }
        internal Type SqlXmlType { get; private set; }
 
        private readonly Func<object, bool> sqlBooleanToBoolean;
        internal bool SqlBooleanToBoolean(object sqlBooleanValue)
        {
            return this.sqlBooleanToBoolean(sqlBooleanValue);
        }
 
        private readonly Func<object, bool?> sqlBooleanToNullableBoolean;
        internal bool? SqlBooleanToNullableBoolean(object sqlBooleanValue)
        {
            if (sqlBooleanToBoolean == null)
            {
                return null;
            }
 
            return this.sqlBooleanToNullableBoolean(sqlBooleanValue);
        }
 
        private readonly Func<byte[], object> sqlBytesFromByteArray;
        internal object SqlBytesFromByteArray(byte[] binaryValue)
        {
            return sqlBytesFromByteArray(binaryValue);
        }
 
        private readonly Func<object, byte[]> sqlBytesToByteArray;
        internal byte[] SqlBytesToByteArray(object sqlBytesValue)
        {
            if (sqlBytesValue == null)
            {
                return null;
            }
 
            return this.sqlBytesToByteArray(sqlBytesValue);
        }
 
        private readonly Func<string, object> sqlStringFromString;
        internal object SqlStringFromString(string stringValue)
        {
            return sqlStringFromString(stringValue);
        }
 
        private readonly Func<string, object> sqlCharsFromString;
        internal object SqlCharsFromString(string stringValue)
        {
            return sqlCharsFromString(stringValue);
        }
 
        private readonly Func<object, string> sqlCharsToString;
        internal string SqlCharsToString(object sqlCharsValue)
        {
            if (sqlCharsValue == null)
            {
                return null;
            }
            return this.sqlCharsToString(sqlCharsValue);
        }
 
        private readonly Func<object, string> sqlStringToString;
        internal string SqlStringToString(object sqlStringValue)
        {
            if (sqlStringValue == null)
            {
                return null;
            }
            return this.sqlStringToString(sqlStringValue);
        }
 
        private readonly Func<object, double> sqlDoubleToDouble;
        internal double SqlDoubleToDouble(object sqlDoubleValue)
        {
            return this.sqlDoubleToDouble(sqlDoubleValue);
        }
 
        private readonly Func<object, double?> sqlDoubleToNullableDouble;
        internal double? SqlDoubleToNullableDouble(object sqlDoubleValue)
        {
            if (sqlDoubleValue == null)
            {
                return null;
            }
            return this.sqlDoubleToNullableDouble(sqlDoubleValue);
        }
 
        private readonly Func<object, int> sqlInt32ToInt;
        internal int SqlInt32ToInt(object sqlInt32Value)
        {
            return this.sqlInt32ToInt(sqlInt32Value);
        }
 
        private readonly Func<object, int?> sqlInt32ToNullableInt;
        internal int? SqlInt32ToNullableInt(object sqlInt32Value)
        {
            if (sqlInt32Value == null)
            {
                return null;
            }
            return this.sqlInt32ToNullableInt(sqlInt32Value);
        }
 
        private readonly Func<System.Xml.XmlReader, object> sqlXmlFromXmlReader;
        internal object SqlXmlFromString(string stringValue)
        {
            System.Xml.XmlReader xmlReader = XmlReaderFromString(stringValue);
            return sqlXmlFromXmlReader(xmlReader);
        }
 
        private readonly Func<object, string> sqlXmlToString;
        internal string SqlXmlToString(object sqlXmlValue)
        {
            if (sqlXmlValue == null)
            {
                return null;
            }
 
            return sqlXmlToString(sqlXmlValue);
        }
 
        private readonly Func<object, bool> isSqlGeographyNull;
        internal bool IsSqlGeographyNull(object sqlGeographyValue)
        {
            if (sqlGeographyValue == null)
            {
                return true;
            }
 
            return isSqlGeographyNull(sqlGeographyValue);
        }
 
        private readonly Func<object, bool> isSqlGeometryNull;
        internal bool IsSqlGeometryNull(object sqlGeometryValue)
        {
            if (sqlGeometryValue == null)
            {
                return true;
            }
 
            return isSqlGeometryNull(sqlGeometryValue);
        }
 
        private readonly Func<object, object> geographyAsTextZMAsSqlChars;
        internal string GeographyAsTextZM(DbGeography geographyValue)
        {
            if (geographyValue == null)
            {
                return null;
            }
 
            object sqlGeographyValue = ConvertToSqlTypesGeography(geographyValue);
            object chars = this.geographyAsTextZMAsSqlChars(sqlGeographyValue);
            return this.SqlCharsToString(chars);
        }
 
        private readonly Func<object, object> geometryAsTextZMAsSqlChars;
        internal string GeometryAsTextZM(DbGeometry geometryValue)
        {
            if (geometryValue == null)
            {
                return null;
            }
 
            object sqlGeometryValue = ConvertToSqlTypesGeometry(geometryValue);
            object chars = this.geometryAsTextZMAsSqlChars(sqlGeometryValue);
            return this.SqlCharsToString(chars);
        }
        #endregion
 
        #region Spatial Types and Spatial Factory Methods
 
        internal Type SqlGeographyType { get; private set; }
        internal Type SqlGeometryType { get; private set; }
        
        internal object ConvertToSqlTypesGeography(DbGeography geographyValue)
        {
            geographyValue.CheckNull("geographyValue");
            object result = GetSqlTypesSpatialValue(geographyValue.AsSpatialValue(), SqlGeographyType);
            return result;
        }
 
        internal object SqlTypesGeographyFromBinary(byte[] wellKnownBinary, int srid)
        {
            Debug.Assert(wellKnownBinary != null, "Validate WKB before calling SqlTypesGeographyFromBinary");
            return sqlGeographyFromWKBByteArray(wellKnownBinary, srid);
        }
 
        internal object SqlTypesGeographyFromText(string wellKnownText, int srid)
        {
            Debug.Assert(wellKnownText != null, "Validate WKT before calling SqlTypesGeographyFromText");
            return sqlGeographyFromWKTString(wellKnownText, srid);
        }
                
        internal object ConvertToSqlTypesGeometry(DbGeometry geometryValue)
        {
            geometryValue.CheckNull("geometryValue");
            object result = GetSqlTypesSpatialValue(geometryValue.AsSpatialValue(), SqlGeometryType);
            return result;
        }
 
        internal object SqlTypesGeometryFromBinary(byte[] wellKnownBinary, int srid)
        {
            Debug.Assert(wellKnownBinary != null, "Validate WKB before calling SqlTypesGeometryFromBinary");
            return sqlGeometryFromWKBByteArray(wellKnownBinary, srid);
        }
 
        internal object SqlTypesGeometryFromText(string wellKnownText, int srid)
        {
            Debug.Assert(wellKnownText != null, "Validate WKT before calling SqlTypesGeometryFromText");
            return sqlGeometryFromWKTString(wellKnownText, srid);
        }
 
        #endregion
 
        #endregion
 
        private object GetSqlTypesSpatialValue(IDbSpatialValue spatialValue, Type requiredProviderValueType)
        {
            Debug.Assert(spatialValue != null, "Ensure spatial value is non-null before calling GetSqlTypesSpatialValue");
 
            // If the specified value was created by this spatial services implementation, its underlying Microsoft.SqlServer.Types.SqlGeography value is available via the ProviderValue property.
            object providerValue = spatialValue.ProviderValue;
            if (providerValue != null && providerValue.GetType() == requiredProviderValueType)
            {
                return providerValue;
            }
 
            // Otherwise, attempt to retrieve a Well Known Binary, Well Known Text or GML (in descending order of preference) representation of the value that can be used to create an appropriate Microsoft.SqlServer.Types.SqlGeography/SqlGeometry value
 
            int? srid = spatialValue.CoordinateSystemId;
            if (srid.HasValue)
            {                
                // Well Known Binary (WKB)
                byte[] binaryValue = spatialValue.WellKnownBinary;
                if (binaryValue != null)
                {
                    return (spatialValue.IsGeography ? sqlGeographyFromWKBByteArray(binaryValue, srid.Value) : sqlGeometryFromWKBByteArray(binaryValue, srid.Value));
                }
 
                // Well Known Text (WKT)
                string textValue = spatialValue.WellKnownText;
                if (textValue != null)
                {
                    return (spatialValue.IsGeography ? sqlGeographyFromWKTString(textValue, srid.Value) : sqlGeometryFromWKTString(textValue, srid.Value));
                }
 
                // Geography Markup Language (GML), as a string
                string gmlValue = spatialValue.GmlString;
                if (gmlValue != null)
                {
                    var xmlReader = XmlReaderFromString(gmlValue);
                    return (spatialValue.IsGeography ? sqlGeographyFromGMLReader(xmlReader, srid.Value) : sqlGeometryFromGMLReader(xmlReader, srid.Value));
                }
            }
 
            throw spatialValue.NotSqlCompatible();
        }
 
        private static System.Xml.XmlReader XmlReaderFromString(string stringValue)
        {
            return System.Xml.XmlReader.Create(new System.IO.StringReader(stringValue));
        }
 
        private readonly Func<string, int, object> sqlGeographyFromWKTString;
        private readonly Func<byte[], int, object> sqlGeographyFromWKBByteArray;
        private readonly Func<System.Xml.XmlReader, int, object> sqlGeographyFromGMLReader;
 
        private readonly Func<string, int, object> sqlGeometryFromWKTString;
        private readonly Func<byte[], int, object> sqlGeometryFromWKBByteArray;
        private readonly Func<System.Xml.XmlReader, int, object> sqlGeometryFromGMLReader;
 
        #region Expression Compilation Helpers
 
        private static Func<TArg, int, object> CreateStaticConstructorDelegate<TArg>(Type spatialType, string methodName)
        {
            Debug.Assert(spatialType != null, "Ensure spatialType is non-null before calling CreateStaticConstructorDelegate");
            var dataParam = Expression.Parameter(typeof(TArg));
            var sridParam = Expression.Parameter(typeof(int));
            var staticCtorMethod = spatialType.GetMethod(methodName, BindingFlags.Public | BindingFlags.Static);
            Debug.Assert(staticCtorMethod != null, "Could not find method '" + methodName + "' on type '" + spatialType.FullName + "'");
            Debug.Assert(staticCtorMethod.GetParameters().Length == 2 && staticCtorMethod.GetParameters()[1].ParameterType == typeof(int), "Static constructor method on '" + spatialType.FullName + "' does not match static constructor pattern?");
 
            Expression sqlData = BuildConvertToSqlType(dataParam, staticCtorMethod.GetParameters()[0].ParameterType);
 
            var ex = Expression.Lambda<Func<TArg, int, object>>(Expression.Call(null, staticCtorMethod, sqlData, sridParam), dataParam, sridParam);
            var result = ex.Compile();
            return result;
        }
 
        private static Expression BuildConvertToSqlType(Expression toConvert, Type convertTo)
        {
            if (toConvert.Type == typeof(byte[]))
            {
                return BuildConvertToSqlBytes(toConvert, convertTo);
            }
            else if (toConvert.Type == typeof(string))
            {
                if (convertTo.Name == "SqlString")
                {
                    return BuildConvertToSqlString(toConvert, convertTo);
                }
                else
                {
                    return BuildConvertToSqlChars(toConvert, convertTo);
                }
            }
            else
            {
                Debug.Assert(toConvert.Type == typeof(System.Xml.XmlReader), "Argument to static constructor method was not byte[], string or XmlReader?");
                if (toConvert.Type == typeof(System.Xml.XmlReader))
                {
                    return BuildConvertToSqlXml(toConvert, convertTo);
                }
            }
 
            return toConvert;
        }
 
        private static Expression BuildConvertToSqlBytes(Expression toConvert, Type sqlBytesType)
        {
            // dataParam:byte[] => new SqlBytes(dataParam)
            Debug.Assert(sqlBytesType.Name == "SqlBytes", "byte[] argument used with non-SqlBytes static constructor method?");
            ConstructorInfo byteArrayCtor = sqlBytesType.GetConstructor(BindingFlags.Instance | BindingFlags.Public, null, new Type[] { toConvert.Type }, null);
            Debug.Assert(byteArrayCtor != null, "SqlXml(System.IO.Stream) constructor not found?");
            Expression result = Expression.New(byteArrayCtor, toConvert);
            return result;
        }
 
        private static Expression BuildConvertToSqlChars(Expression toConvert, Type sqlCharsType)
        {
            // dataParam:String => new SqlChars(new SqlString(dataParam))
            Debug.Assert(sqlCharsType.Name == "SqlChars", "String argument used with non-SqlChars static constructor method?");
            Type sqlString = sqlCharsType.Assembly.GetType("System.Data.SqlTypes.SqlString", throwOnError: true);
            ConstructorInfo sqlCharsFromSqlStringCtor = sqlCharsType.GetConstructor(BindingFlags.Instance | BindingFlags.Public, null, new Type[] { sqlString }, null);
            Debug.Assert(sqlCharsFromSqlStringCtor != null, "SqlXml(System.IO.Stream) constructor not found?");
            ConstructorInfo sqlStringFromStringCtor = sqlString.GetConstructor(BindingFlags.Instance | BindingFlags.Public, null, new Type[] { typeof(string) }, null);
            Expression result = Expression.New(sqlCharsFromSqlStringCtor, Expression.New(sqlStringFromStringCtor, toConvert));
            return result;
        }
 
        private static Expression BuildConvertToSqlString(Expression toConvert, Type sqlStringType)
        {
            // dataParam:String => new SqlString(dataParam)
            Debug.Assert(sqlStringType.Name == "SqlString", "String argument used with non-SqlString static constructor method?");
            ConstructorInfo sqlStringFromStringCtor = sqlStringType.GetConstructor(BindingFlags.Instance | BindingFlags.Public, null, new Type[] { typeof(string) }, null);
            Debug.Assert(sqlStringFromStringCtor != null);
            Expression result = Expression.Convert(Expression.New(sqlStringFromStringCtor, toConvert), typeof(object));
            return result;
        }
 
        private static Expression BuildConvertToSqlXml(Expression toConvert, Type sqlXmlType)
        {
            // dataParam:Stream => new SqlXml(dataParam)
            Debug.Assert(sqlXmlType.Name == "SqlXml", "Stream argument used with non-SqlXml static constructor method?");
            ConstructorInfo readerCtor = sqlXmlType.GetConstructor(BindingFlags.Instance | BindingFlags.Public, null, new Type[] { toConvert.Type }, null);
            Debug.Assert(readerCtor != null, "SqlXml(System.Xml.XmlReader) constructor not found?");
            Expression result = Expression.New(readerCtor, toConvert);
            return result;
        }
 
        #endregion
    }
}