Mercurial > pub > bltoolkit
diff Source/Data/Sql/SqlDataType.cs @ 0:f990fcb411a9
Копия текущей версии из github
author | cin |
---|---|
date | Thu, 27 Mar 2014 21:46:09 +0400 |
parents | |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Source/Data/Sql/SqlDataType.cs Thu Mar 27 21:46:09 2014 +0400 @@ -0,0 +1,555 @@ +using System; +using System.Collections.Generic; +using System.Data; +using System.Data.SqlTypes; +using System.Linq; +using System.Text; + +namespace BLToolkit.Data.Sql +{ + using Reflection; + + public class SqlDataType : ISqlExpression + { + #region Init + + public SqlDataType(SqlDbType dbType) + { + var defaultType = GetDataType(dbType); + + SqlDbType = dbType; + Type = defaultType.Type; + Length = defaultType.Length; + Precision = defaultType.Precision; + Scale = defaultType.Scale; + } + + public SqlDataType(SqlDbType dbType, int length) + { + if (length <= 0) throw new ArgumentOutOfRangeException("length"); + + SqlDbType = dbType; + Type = GetDataType(dbType).Type; + Length = length; + } + + public SqlDataType(SqlDbType dbType, int precision, int scale) + { + if (precision <= 0) throw new ArgumentOutOfRangeException("precision"); + if (scale < 0) throw new ArgumentOutOfRangeException("scale"); + + SqlDbType = dbType; + Type = GetDataType(dbType).Type; + Precision = precision; + Scale = scale; + } + + public SqlDataType([JetBrains.Annotations.NotNull]Type type) + { + if (type == null) throw new ArgumentNullException("type"); + + var defaultType = GetDataType(type); + + SqlDbType = defaultType.SqlDbType; + Type = type; + Length = defaultType.Length; + Precision = defaultType.Precision; + Scale = defaultType.Scale; + } + + public SqlDataType([JetBrains.Annotations.NotNull] Type type, int length) + { + if (type == null) throw new ArgumentNullException ("type"); + if (length <= 0) throw new ArgumentOutOfRangeException("length"); + + SqlDbType = GetDataType(type).SqlDbType; + Type = type; + Length = length; + } + + public SqlDataType([JetBrains.Annotations.NotNull] Type type, int precision, int scale) + { + if (type == null) throw new ArgumentNullException ("type"); + if (precision <= 0) throw new ArgumentOutOfRangeException("precision"); + if (scale < 0) throw new ArgumentOutOfRangeException("scale"); + + SqlDbType = GetDataType(type).SqlDbType; + Type = type; + Precision = precision; + Scale = scale; + } + + public SqlDataType(SqlDbType dbType, [JetBrains.Annotations.NotNull]Type type) + { + if (type == null) throw new ArgumentNullException("type"); + + var defaultType = GetDataType(dbType); + + SqlDbType = dbType; + Type = type; + Length = defaultType.Length; + Precision = defaultType.Precision; + Scale = defaultType.Scale; + } + + public SqlDataType(SqlDbType dbType, [JetBrains.Annotations.NotNull] Type type, int length) + { + if (type == null) throw new ArgumentNullException ("type"); + if (length <= 0) throw new ArgumentOutOfRangeException("length"); + + SqlDbType = dbType; + Type = type; + Length = length; + } + + public SqlDataType(SqlDbType dbType, [JetBrains.Annotations.NotNull] Type type, int precision, int scale) + { + if (type == null) throw new ArgumentNullException ("type"); + if (precision <= 0) throw new ArgumentOutOfRangeException("precision"); + if (scale < 0) throw new ArgumentOutOfRangeException("scale"); + + SqlDbType = dbType; + Type = type; + Precision = precision; + Scale = scale; + } + + #endregion + + #region Public Members + + public SqlDbType SqlDbType { get; private set; } + public Type Type { get; private set; } + public int Length { get; private set; } + public int Precision { get; private set; } + public int Scale { get; private set; } + + #endregion + + #region Static Members + + struct TypeInfo + { + public TypeInfo(SqlDbType dbType, int maxLength, int maxPrecision, int maxScale, int maxDisplaySize) + { + SqlDbType = dbType; + MaxLength = maxLength; + MaxPrecision = maxPrecision; + MaxScale = maxScale; + MaxDisplaySize = maxDisplaySize; + } + + public readonly SqlDbType SqlDbType; + public readonly int MaxLength; + public readonly int MaxPrecision; + public readonly int MaxScale; + public readonly int MaxDisplaySize; + } + + static TypeInfo[] SortTypeInfo(params TypeInfo[] info) + { + var sortedInfo = new TypeInfo[info.Max(ti => (int)ti.SqlDbType) + 1]; + + foreach (var typeInfo in info) + sortedInfo[(int)typeInfo.SqlDbType] = typeInfo; + + return sortedInfo; + } + + static int Len(object obj) + { + return obj.ToString().Length; + } + + static readonly TypeInfo[] _typeInfo = SortTypeInfo + ( + // DbType MaxLength MaxPrecision MaxScale MaxDisplaySize + // + new TypeInfo(SqlDbType.BigInt, 8, Len( long.MaxValue), 0, Len( long.MinValue)), + new TypeInfo(SqlDbType.Int, 4, Len( int.MaxValue), 0, Len( int.MinValue)), + new TypeInfo(SqlDbType.SmallInt, 2, Len(short.MaxValue), 0, Len(short.MinValue)), + new TypeInfo(SqlDbType.TinyInt, 1, Len( byte.MaxValue), 0, Len( byte.MaxValue)), + new TypeInfo(SqlDbType.Bit, 1, 1, 0, 1), + + new TypeInfo(SqlDbType.Decimal, 17, Len(decimal.MaxValue), Len(decimal.MaxValue), Len(decimal.MinValue)+1), + new TypeInfo(SqlDbType.Money, 8, 19, 4, 19 + 2), + new TypeInfo(SqlDbType.SmallMoney, 4, 10, 4, 10 + 2), + new TypeInfo(SqlDbType.Float, 8, 15, 15, 15 + 2 + 5), + new TypeInfo(SqlDbType.Real, 4, 7, 7, 7 + 2 + 4), + + new TypeInfo(SqlDbType.DateTime, 8, -1, -1, 23), +#if !MONO + new TypeInfo(SqlDbType.DateTime2, 8, -1, -1, 27), +#endif + new TypeInfo(SqlDbType.SmallDateTime, 4, -1, -1, 19), + new TypeInfo(SqlDbType.Date, 3, -1, -1, 10), + new TypeInfo(SqlDbType.Time, 5, -1, -1, 16), +#if !MONO + new TypeInfo(SqlDbType.DateTimeOffset, 10, -1, -1, 34), +#endif + + new TypeInfo(SqlDbType.Char, 8000, -1, -1, 8000), + new TypeInfo(SqlDbType.VarChar, 8000, -1, -1, 8000), + new TypeInfo(SqlDbType.Text, int.MaxValue, -1, -1, int.MaxValue), + new TypeInfo(SqlDbType.NChar, 4000, -1, -1, 4000), + new TypeInfo(SqlDbType.NVarChar, 4000, -1, -1, 4000), + new TypeInfo(SqlDbType.NText, int.MaxValue, -1, -1, int.MaxValue / 2), + + new TypeInfo(SqlDbType.Binary, 8000, -1, -1, -1), + new TypeInfo(SqlDbType.VarBinary, 8000, -1, -1, -1), + new TypeInfo(SqlDbType.Image, int.MaxValue, -1, -1, -1), + + new TypeInfo(SqlDbType.Timestamp, 8, -1, -1, -1), + new TypeInfo(SqlDbType.UniqueIdentifier, 16, -1, -1, 36), + + new TypeInfo(SqlDbType.Variant, -1, -1, -1, -1), + new TypeInfo(SqlDbType.Xml, -1, -1, -1, -1), +#if !MONO + new TypeInfo(SqlDbType.Structured, -1, -1, -1, -1), +#endif + new TypeInfo(SqlDbType.Udt, -1, -1, -1, -1) + ); + + public static int GetMaxLength (SqlDbType dbType) { return _typeInfo[(int)dbType].MaxLength; } + public static int GetMaxPrecision (SqlDbType dbType) { return _typeInfo[(int)dbType].MaxPrecision; } + public static int GetMaxScale (SqlDbType dbType) { return _typeInfo[(int)dbType].MaxScale; } + public static int GetMaxDisplaySize(SqlDbType dbType) { return _typeInfo[(int)dbType].MaxDisplaySize; } + + public static SqlDataType GetDataType(Type type) + { + var underlyingType = type; + + if (underlyingType.IsGenericType && underlyingType.GetGenericTypeDefinition() == typeof(Nullable<>)) + underlyingType = underlyingType.GetGenericArguments()[0]; + + if (underlyingType.IsEnum) + underlyingType = Enum.GetUnderlyingType(underlyingType); + + switch (Type.GetTypeCode(underlyingType)) + { + case TypeCode.Boolean : return Boolean; + case TypeCode.Char : return Char; + case TypeCode.SByte : return SByte; + case TypeCode.Byte : return Byte; + case TypeCode.Int16 : return Int16; + case TypeCode.UInt16 : return UInt16; + case TypeCode.Int32 : return Int32; + case TypeCode.UInt32 : return UInt32; + case TypeCode.Int64 : return Int64; + case TypeCode.UInt64 : return UInt64; + case TypeCode.Single : return Single; + case TypeCode.Double : return Double; + case TypeCode.Decimal : return Decimal; + case TypeCode.DateTime : return DateTime; + case TypeCode.String : return String; + case TypeCode.Object : + if (underlyingType == typeof(Guid)) return Guid; + if (underlyingType == typeof(byte[])) return ByteArray; + if (underlyingType == typeof(System.Data.Linq.Binary)) return LinqBinary; + if (underlyingType == typeof(char[])) return CharArray; +#if !MONO + if (underlyingType == typeof(DateTimeOffset)) return DateTimeOffset; +#endif + if (underlyingType == typeof(TimeSpan)) return TimeSpan; + break; + + case TypeCode.DBNull : + case TypeCode.Empty : + default : break; + } + +#if !SILVERLIGHT + + if (underlyingType == typeof(SqlByte)) return SqlByte; + if (underlyingType == typeof(SqlInt16)) return SqlInt16; + if (underlyingType == typeof(SqlInt32)) return SqlInt32; + if (underlyingType == typeof(SqlInt64)) return SqlInt64; + if (underlyingType == typeof(SqlSingle)) return SqlSingle; + if (underlyingType == typeof(SqlBoolean)) return SqlBoolean; + if (underlyingType == typeof(SqlDouble)) return SqlDouble; + if (underlyingType == typeof(SqlDateTime)) return SqlDateTime; + if (underlyingType == typeof(SqlDecimal)) return SqlDecimal; + if (underlyingType == typeof(SqlMoney)) return SqlMoney; + if (underlyingType == typeof(SqlString)) return SqlString; + if (underlyingType == typeof(SqlBinary)) return SqlBinary; + if (underlyingType == typeof(SqlGuid)) return SqlGuid; + if (underlyingType == typeof(SqlBytes)) return SqlBytes; + if (underlyingType == typeof(SqlChars)) return SqlChars; + if (underlyingType == typeof(SqlXml)) return SqlXml; + +#endif + + return DbVariant; + } + + public static SqlDataType GetDataType(SqlDbType type) + { + switch (type) + { + case SqlDbType.BigInt : return DbBigInt; + case SqlDbType.Binary : return DbBinary; + case SqlDbType.Bit : return DbBit; + case SqlDbType.Char : return DbChar; + case SqlDbType.DateTime : return DbDateTime; + case SqlDbType.Decimal : return DbDecimal; + case SqlDbType.Float : return DbFloat; + case SqlDbType.Image : return DbImage; + case SqlDbType.Int : return DbInt; + case SqlDbType.Money : return DbMoney; + case SqlDbType.NChar : return DbNChar; + case SqlDbType.NText : return DbNText; + case SqlDbType.NVarChar : return DbNVarChar; + case SqlDbType.Real : return DbReal; + case SqlDbType.UniqueIdentifier : return DbUniqueIdentifier; + case SqlDbType.SmallDateTime : return DbSmallDateTime; + case SqlDbType.SmallInt : return DbSmallInt; + case SqlDbType.SmallMoney : return DbSmallMoney; + case SqlDbType.Text : return DbText; + case SqlDbType.Timestamp : return DbTimestamp; + case SqlDbType.TinyInt : return DbTinyInt; + case SqlDbType.VarBinary : return DbVarBinary; + case SqlDbType.VarChar : return DbVarChar; + case SqlDbType.Variant : return DbVariant; +#if !SILVERLIGHT + case SqlDbType.Xml : return DbXml; +#endif + case SqlDbType.Udt : return DbUdt; +#if !MONO + case SqlDbType.Structured : return DbStructured; +#endif + case SqlDbType.Date : return DbDate; + case SqlDbType.Time : return DbTime; +#if !MONO + case SqlDbType.DateTime2 : return DbDateTime2; + case SqlDbType.DateTimeOffset : return DbDateTimeOffset; +#endif + } + + throw new InvalidOperationException(); + } + + public static bool CanBeNull(Type type) + { + if (type.IsValueType == false || + type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>) || + TypeHelper.IsSameOrParent(typeof(INullable), type)) + return true; + + return false; + } + + #endregion + + #region Default Types + + internal SqlDataType(SqlDbType dbType, Type type, int length, int precision, int scale) + { + SqlDbType = dbType; + Type = type; + Length = length; + Precision = precision; + Scale = scale; + } + + SqlDataType(SqlDbType dbType, Type type, Func<SqlDbType,int> length, int precision, int scale) + : this(dbType, type, length(dbType), precision, scale) + { + } + + SqlDataType(SqlDbType dbType, Type type, int length, Func<SqlDbType,int> precision, int scale) + : this(dbType, type, length, precision(dbType), scale) + { + } + + public static readonly SqlDataType DbBigInt = new SqlDataType(SqlDbType.BigInt, typeof(Int64), 0, 0, 0); + public static readonly SqlDataType DbInt = new SqlDataType(SqlDbType.Int, typeof(Int32), 0, 0, 0); + public static readonly SqlDataType DbSmallInt = new SqlDataType(SqlDbType.SmallInt, typeof(Int16), 0, 0, 0); + public static readonly SqlDataType DbTinyInt = new SqlDataType(SqlDbType.TinyInt, typeof(Byte), 0, 0, 0); + public static readonly SqlDataType DbBit = new SqlDataType(SqlDbType.Bit, typeof(Boolean), 0, 0, 0); + + public static readonly SqlDataType DbDecimal = new SqlDataType(SqlDbType.Decimal, typeof(Decimal), 0, GetMaxPrecision, 10); + public static readonly SqlDataType DbMoney = new SqlDataType(SqlDbType.Money, typeof(Decimal), 0, GetMaxPrecision, 4); + public static readonly SqlDataType DbSmallMoney = new SqlDataType(SqlDbType.SmallMoney, typeof(Decimal), 0, GetMaxPrecision, 4); + public static readonly SqlDataType DbFloat = new SqlDataType(SqlDbType.Float, typeof(Double), 0, 0, 0); + public static readonly SqlDataType DbReal = new SqlDataType(SqlDbType.Real, typeof(Single), 0, 0, 0); + + public static readonly SqlDataType DbDateTime = new SqlDataType(SqlDbType.DateTime, typeof(DateTime), 0, 0, 0); +#if !MONO + public static readonly SqlDataType DbDateTime2 = new SqlDataType(SqlDbType.DateTime2, typeof(DateTime), 0, 0, 0); +#else + public static readonly SqlDataType DbDateTime2 = new SqlDataType(SqlDbType.DateTime, typeof(DateTime), 0, 0, 0); +#endif + public static readonly SqlDataType DbSmallDateTime = new SqlDataType(SqlDbType.SmallDateTime, typeof(DateTime), 0, 0, 0); + public static readonly SqlDataType DbDate = new SqlDataType(SqlDbType.Date, typeof(DateTime), 0, 0, 0); + public static readonly SqlDataType DbTime = new SqlDataType(SqlDbType.Time, typeof(TimeSpan), 0, 0, 0); +#if !MONO + public static readonly SqlDataType DbDateTimeOffset = new SqlDataType(SqlDbType.DateTimeOffset, typeof(DateTimeOffset), 0, 0, 0); +#endif + + public static readonly SqlDataType DbChar = new SqlDataType(SqlDbType.Char, typeof(String), GetMaxLength, 0, 0); + public static readonly SqlDataType DbVarChar = new SqlDataType(SqlDbType.VarChar, typeof(String), GetMaxLength, 0, 0); + public static readonly SqlDataType DbText = new SqlDataType(SqlDbType.Text, typeof(String), GetMaxLength, 0, 0); + public static readonly SqlDataType DbNChar = new SqlDataType(SqlDbType.NChar, typeof(String), GetMaxLength, 0, 0); + public static readonly SqlDataType DbNVarChar = new SqlDataType(SqlDbType.NVarChar, typeof(String), GetMaxLength, 0, 0); + public static readonly SqlDataType DbNText = new SqlDataType(SqlDbType.NText, typeof(String), GetMaxLength, 0, 0); + + public static readonly SqlDataType DbBinary = new SqlDataType(SqlDbType.Binary, typeof(Byte[]), GetMaxLength, 0, 0); + public static readonly SqlDataType DbVarBinary = new SqlDataType(SqlDbType.VarBinary, typeof(Byte[]), GetMaxLength, 0, 0); + public static readonly SqlDataType DbImage = new SqlDataType(SqlDbType.Image, typeof(Byte[]), GetMaxLength, 0, 0); + + public static readonly SqlDataType DbTimestamp = new SqlDataType(SqlDbType.Timestamp, typeof(Byte[]), 0, 0, 0); + public static readonly SqlDataType DbUniqueIdentifier = new SqlDataType(SqlDbType.UniqueIdentifier, typeof(Guid), 0, 0, 0); + + public static readonly SqlDataType DbVariant = new SqlDataType(SqlDbType.Variant, typeof(Object), 0, 0, 0); +#if !SILVERLIGHT + public static readonly SqlDataType DbXml = new SqlDataType(SqlDbType.Xml, typeof(SqlXml), 0, 0, 0); +#endif + public static readonly SqlDataType DbUdt = new SqlDataType(SqlDbType.Udt, typeof(Object), 0, 0, 0); +#if !MONO + public static readonly SqlDataType DbStructured = new SqlDataType(SqlDbType.Structured, typeof(Object), 0, 0, 0); +#endif + + public static readonly SqlDataType Boolean = DbBit; + public static readonly SqlDataType Char = new SqlDataType(SqlDbType.Char, typeof(Char), 1, 0, 0); + public static readonly SqlDataType SByte = new SqlDataType(SqlDbType.SmallInt, typeof(SByte), 0, 0, 0); + public static readonly SqlDataType Byte = DbTinyInt; + public static readonly SqlDataType Int16 = DbSmallInt; + public static readonly SqlDataType UInt16 = new SqlDataType(SqlDbType.Int, typeof(UInt16), 0, 0, 0); + public static readonly SqlDataType Int32 = DbInt; + public static readonly SqlDataType UInt32 = new SqlDataType(SqlDbType.BigInt, typeof(UInt32), 0, 0, 0); + public static readonly SqlDataType Int64 = DbBigInt; + public static readonly SqlDataType UInt64 = new SqlDataType(SqlDbType.Decimal, typeof(UInt64), 0, ulong.MaxValue.ToString().Length, 0); + public static readonly SqlDataType Single = DbReal; + public static readonly SqlDataType Double = DbFloat; + public static readonly SqlDataType Decimal = DbDecimal; + public static readonly SqlDataType DateTime = DbDateTime2; + public static readonly SqlDataType String = DbNVarChar; + public static readonly SqlDataType Guid = DbUniqueIdentifier; + public static readonly SqlDataType ByteArray = DbVarBinary; + public static readonly SqlDataType LinqBinary = DbVarBinary; + public static readonly SqlDataType CharArray = new SqlDataType(SqlDbType.NVarChar, typeof(Char[]), GetMaxLength, 0, 0); +#if !MONO + public static readonly SqlDataType DateTimeOffset = DbDateTimeOffset; +#endif + public static readonly SqlDataType TimeSpan = DbTime; + +#if !SILVERLIGHT + public static readonly SqlDataType SqlByte = new SqlDataType(SqlDbType.TinyInt, typeof(SqlByte), 0, 0, 0); + public static readonly SqlDataType SqlInt16 = new SqlDataType(SqlDbType.SmallInt, typeof(SqlInt16), 0, 0, 0); + public static readonly SqlDataType SqlInt32 = new SqlDataType(SqlDbType.Int, typeof(SqlInt32), 0, 0, 0); + public static readonly SqlDataType SqlInt64 = new SqlDataType(SqlDbType.BigInt, typeof(SqlInt64), 0, 0, 0); + public static readonly SqlDataType SqlSingle = new SqlDataType(SqlDbType.Real, typeof(SqlSingle), 0, 0, 0); + public static readonly SqlDataType SqlBoolean = new SqlDataType(SqlDbType.Bit, typeof(SqlBoolean), 0, 0, 0); + public static readonly SqlDataType SqlDouble = new SqlDataType(SqlDbType.Float, typeof(SqlDouble), 0, 0, 0); + public static readonly SqlDataType SqlDateTime = new SqlDataType(SqlDbType.DateTime, typeof(SqlDateTime), 0, 0, 0); + public static readonly SqlDataType SqlDecimal = new SqlDataType(SqlDbType.Decimal, typeof(SqlDecimal), 0, GetMaxPrecision, 10); + public static readonly SqlDataType SqlMoney = new SqlDataType(SqlDbType.Money, typeof(SqlMoney), 0, GetMaxPrecision, 4); + public static readonly SqlDataType SqlString = new SqlDataType(SqlDbType.NVarChar, typeof(SqlString), GetMaxLength, 0, 0); + public static readonly SqlDataType SqlBinary = new SqlDataType(SqlDbType.Binary, typeof(SqlBinary), GetMaxLength, 0, 0); + public static readonly SqlDataType SqlGuid = new SqlDataType(SqlDbType.UniqueIdentifier, typeof(SqlGuid), 0, 0, 0); + public static readonly SqlDataType SqlBytes = new SqlDataType(SqlDbType.Image, typeof(SqlBytes), GetMaxLength, 0, 0); + public static readonly SqlDataType SqlChars = new SqlDataType(SqlDbType.Text, typeof(SqlChars), GetMaxLength, 0, 0); + public static readonly SqlDataType SqlXml = new SqlDataType(SqlDbType.Xml, typeof(SqlXml), 0, 0, 0); +#endif + + #endregion + + #region Overrides + +#if OVERRIDETOSTRING + + public override string ToString() + { + return ((IQueryElement)this).ToString(new StringBuilder(), new Dictionary<IQueryElement,IQueryElement>()).ToString(); + } + +#endif + + #endregion + + #region ISqlExpression Members + + public int Precedence + { + get { return Sql.Precedence.Primary; } + } + + public Type SystemType + { + get { return typeof(Type); } + } + + #endregion + + #region ISqlExpressionWalkable Members + + ISqlExpression ISqlExpressionWalkable.Walk(bool skipColumns, Func<ISqlExpression,ISqlExpression> func) + { + return func(this); + } + + #endregion + + #region IEquatable<ISqlExpression> Members + + bool IEquatable<ISqlExpression>.Equals(ISqlExpression other) + { + if (this == other) + return true; + + var value = (SqlDataType)other; + return Type == value.Type && Length == value.Length && Precision == value.Precision && Scale == value.Scale; + } + + #endregion + + #region ISqlExpression Members + + public bool CanBeNull() + { + return false; + } + + public bool Equals(ISqlExpression other, Func<ISqlExpression,ISqlExpression,bool> comparer) + { + return ((ISqlExpression)this).Equals(other) && comparer(this, other); + } + + #endregion + + #region ICloneableElement Members + + public ICloneableElement Clone(Dictionary<ICloneableElement, ICloneableElement> objectTree, Predicate<ICloneableElement> doClone) + { + if (!doClone(this)) + return this; + + ICloneableElement clone; + + if (!objectTree.TryGetValue(this, out clone)) + objectTree.Add(this, clone = new SqlDataType(SqlDbType, Type, Length, Precision, Scale)); + + return clone; + } + + #endregion + + #region IQueryElement Members + + public QueryElementType ElementType { get { return QueryElementType.SqlDataType; } } + + StringBuilder IQueryElement.ToString(StringBuilder sb, Dictionary<IQueryElement,IQueryElement> dic) + { + sb.Append(SqlDbType); + + if (Length != 0) + sb.Append('(').Append(Length).Append(')'); + else if (Precision != 0) + sb.Append('(').Append(Precision).Append(',').Append(Scale).Append(')'); + + return sb; + } + + #endregion + } +}