//------------------------------------------------------------------------------ // // Copyright (c) Microsoft Corporation. All rights reserved. // // [....] // [....] //------------------------------------------------------------------------------ namespace System.Data.Common { using System; using System.Data; using System.Xml; using System.IO; using System.Xml.Serialization; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Runtime.CompilerServices; internal sealed class ObjectStorage : DataStorage { static private readonly Object defaultValue = null; private enum Families { DATETIME, NUMBER, STRING, BOOLEAN, ARRAY }; private object[] values; private readonly bool implementsIXmlSerializable; internal ObjectStorage(DataColumn column, Type type) : base(column, type, defaultValue, DBNull.Value, typeof(ICloneable).IsAssignableFrom(type), GetStorageType(type)) { implementsIXmlSerializable = typeof(IXmlSerializable).IsAssignableFrom(type); } override public Object Aggregate(int[] records, AggregateType kind) { throw ExceptionBuilder.AggregateException(kind, DataType); } override public int Compare(int recordNo1, int recordNo2) { object valueNo1 = values[recordNo1]; object valueNo2 = values[recordNo2]; if (valueNo1 == valueNo2) return 0; if (valueNo1 == null) return -1; if (valueNo2 == null) return 1; IComparable icomparable = (valueNo1 as IComparable); if (null != icomparable) { try { return icomparable.CompareTo(valueNo2); } catch(ArgumentException e) { ExceptionBuilder.TraceExceptionWithoutRethrow(e); } } return CompareWithFamilies(valueNo1, valueNo2); } override public int CompareValueTo(int recordNo1, Object value) { object valueNo1 = Get(recordNo1); if (valueNo1 is IComparable) { if (value.GetType() == valueNo1.GetType()) return((IComparable) valueNo1).CompareTo(value); } if (valueNo1 == value) return 0; if (valueNo1 == null) { if (NullValue == value) { return 0; } return -1; } if ((NullValue == value) || (null == value)) { return 1; } return CompareWithFamilies(valueNo1, value); } private int CompareTo(object valueNo1, object valueNo2) { if (valueNo1 == null) return -1; if (valueNo2 == null) return 1; if (valueNo1 == valueNo2) return 0; if (valueNo1 == NullValue) return -1; if (valueNo2 == NullValue) return 1; if (valueNo1 is IComparable) { try{ return ((IComparable) valueNo1).CompareTo(valueNo2); } catch(ArgumentException e) { ExceptionBuilder.TraceExceptionWithoutRethrow(e); } } return CompareWithFamilies(valueNo1, valueNo2); } private int CompareWithFamilies(Object valueNo1, Object valueNo2) { Families Family1 = GetFamily(valueNo1.GetType()); Families Family2 = GetFamily(valueNo2.GetType()); if (Family1 < Family2) return -1; else if (Family1 > Family2) return 1; else { switch (Family1) { case Families.BOOLEAN : valueNo1 = Convert.ToBoolean(valueNo1, FormatProvider); valueNo2 = Convert.ToBoolean(valueNo2, FormatProvider); break; case Families.DATETIME: valueNo1 = Convert.ToDateTime(valueNo1, FormatProvider); valueNo2 = Convert.ToDateTime(valueNo1, FormatProvider); break; case Families.NUMBER : valueNo1 = Convert.ToDouble(valueNo1, FormatProvider); valueNo2 = Convert.ToDouble(valueNo2, FormatProvider); break; case Families.ARRAY :{ Array arr1 = (Array) valueNo1; Array arr2 = (Array) valueNo2; if (arr1.Length > arr2.Length) return 1; else if (arr1.Length < arr2.Length) return -1; else { // same number of elements for (int i = 0; i < arr1.Length; i++){ int c = CompareTo(arr1.GetValue(i),arr2.GetValue(i)); if (c != 0) return c ; } } return 0; } default : valueNo1 = valueNo1.ToString(); valueNo2 = valueNo2.ToString(); break; } return ((IComparable) valueNo1).CompareTo(valueNo2); } } override public void Copy(int recordNo1, int recordNo2) { values[recordNo2] = values[recordNo1]; } override public Object Get(int recordNo) { Object value = values[recordNo]; if (null != value) { return value; } return NullValue; } private Families GetFamily(Type dataType) { switch (Type.GetTypeCode(dataType)) { case TypeCode.Boolean: return Families.BOOLEAN; case TypeCode.Char: return Families.STRING; case TypeCode.SByte: return Families.STRING; case TypeCode.Byte: return Families.STRING; case TypeCode.Int16: return Families.NUMBER; case TypeCode.UInt16: return Families.NUMBER; case TypeCode.Int32: return Families.NUMBER; case TypeCode.UInt32: return Families.NUMBER; case TypeCode.Int64: return Families.NUMBER; case TypeCode.UInt64: return Families.NUMBER; case TypeCode.Single: return Families.NUMBER; case TypeCode.Double: return Families.NUMBER; case TypeCode.Decimal: return Families.NUMBER; case TypeCode.DateTime: return Families.DATETIME; case TypeCode.String: return Families.STRING; default: if (typeof(TimeSpan) == dataType) { return Families.DATETIME; } else if(dataType.IsArray) { return Families.ARRAY; } else{ return Families.STRING; } } } override public bool IsNull(int record) { return (null == values[record]); } override public void Set(int recordNo, Object value) { System.Diagnostics.Debug.Assert(null != value, "null value"); if (NullValue == value) { values[recordNo] = null; } else if (DataType == typeof(Object) || DataType.IsInstanceOfType(value)) { values[recordNo] = value; } else { Type valType = value.GetType(); if (DataType == typeof(Guid) && valType == typeof(string)){ values[recordNo] = new Guid((string)value); } else if (DataType == typeof(byte[])) { if (valType == typeof(Boolean)){ values[recordNo] = BitConverter.GetBytes((Boolean)value); } else if (valType == typeof(Char)){ values[recordNo] = BitConverter.GetBytes((Char)value); } else if (valType == typeof(Int16)){ values[recordNo] = BitConverter.GetBytes((Int16)value); } else if (valType == typeof(Int32)){ values[recordNo] = BitConverter.GetBytes((Int32)value); } else if (valType == typeof(Int64)){ values[recordNo] = BitConverter.GetBytes((Int64)value); } else if (valType == typeof(UInt16)){ values[recordNo] = BitConverter.GetBytes((UInt16)value); } else if (valType == typeof(UInt32)){ values[recordNo] = BitConverter.GetBytes((UInt32)value); } else if (valType == typeof(UInt64)){ values[recordNo] = BitConverter.GetBytes((UInt64)value); } else if (valType == typeof(Single)){ values[recordNo] = BitConverter.GetBytes((Single)value); } else if (valType == typeof(Double)){ values[recordNo] = BitConverter.GetBytes((Double)value); } else { throw ExceptionBuilder.StorageSetFailed(); } } else { throw ExceptionBuilder.StorageSetFailed(); } } } override public void SetCapacity(int capacity) { object[] newValues = new object[capacity]; if (values != null) { Array.Copy(values, 0, newValues, 0, Math.Min(capacity, values.Length)); } values = newValues; } // Prevent inlining so that reflection calls are not moved to caller that may be in a different assembly that may have a different grant set. [MethodImpl(MethodImplOptions.NoInlining)] override public object ConvertXmlToObject(string s) { Type type = DataType; // real type of objects in this column if (type == typeof(byte[])) { return Convert.FromBase64String(s); } if (type == typeof(Type)){ return Type.GetType(s); } if (type == typeof (Guid)){ return (new Guid(s)); } if (type == typeof (Uri)){ return (new Uri(s)); } if (implementsIXmlSerializable) { object Obj = System.Activator.CreateInstance(DataType, true); StringReader strReader = new StringReader(s); using (XmlTextReader xmlTextReader = new XmlTextReader(strReader)) { ((IXmlSerializable)Obj).ReadXml(xmlTextReader); } return Obj; } StringReader strreader = new StringReader(s); XmlSerializer deserializerWithOutRootAttribute = ObjectStorage.GetXmlSerializer(type); return(deserializerWithOutRootAttribute.Deserialize(strreader)); } // Prevent inlining so that reflection calls are not moved to caller that may be in a different assembly that may have a different grant set. [MethodImpl(MethodImplOptions.NoInlining)] public override object ConvertXmlToObject(XmlReader xmlReader, XmlRootAttribute xmlAttrib) { object retValue = null; bool isBaseCLRType = false; bool legacyUDT = false; // in 1.0 and 1.1 we used to call ToString on CDT obj. so if we have the same case // we need to handle the case when we have column type as object. if (null == xmlAttrib) { // this means type implements IXmlSerializable Type type = null; string typeName = xmlReader.GetAttribute(Keywords.MSD_INSTANCETYPE, Keywords.MSDNS); if (typeName == null || typeName.Length == 0) { // No CDT polumorphism string xsdTypeName = xmlReader.GetAttribute(Keywords.TYPE, Keywords.XSINS); // this xsd type: Base type polymorphism if (null != xsdTypeName && xsdTypeName.Length > 0) { string [] _typename = xsdTypeName.Split(':'); if (_typename.Length == 2) { // split will return aray of size 1 if ":" is not there if (xmlReader.LookupNamespace(_typename[0]) == Keywords.XSDNS) { xsdTypeName = _typename[1]; // trim the prefix and just continue with } } // for other case, let say we have two ':' in type, the we throws (as old behavior) type = XSDSchema.XsdtoClr(xsdTypeName); isBaseCLRType = true; } else if (DataType == typeof(object)) {// there is no Keywords.MSD_INSTANCETYPE and no Keywords.TYPE legacyUDT = true; // see if our type is object } } if (legacyUDT) { // if Everett UDT, just read it and return string retValue = xmlReader.ReadString(); } else { if (typeName == Keywords.TYPEINSTANCE) { retValue = Type.GetType(xmlReader.ReadString()); xmlReader.Read(); // need to move to next node } else { if (null == type) { type = (typeName == null)? DataType : DataStorage.GetType(typeName); } if (type == typeof(char) || type == typeof(Guid)) { //msdata:char and msdata:guid imply base types. isBaseCLRType=true; } if (type == typeof(object)) throw ExceptionBuilder.CanNotDeserializeObjectType(); if (!isBaseCLRType){ retValue = System.Activator.CreateInstance (type, true); Debug.Assert(xmlReader is DataTextReader, "Invalid DataTextReader is being passed to customer"); ((IXmlSerializable)retValue).ReadXml(xmlReader); } else { // Process Base CLR type // for Element Node, if it is Empty, ReadString does not move to End Element; we need to move it if(type == typeof(string) && xmlReader.NodeType == XmlNodeType.Element && xmlReader.IsEmptyElement) { retValue = string.Empty; } else { retValue = xmlReader.ReadString(); if (type != typeof(byte[])) { retValue = SqlConvert.ChangeTypeForXML(retValue, type); } else { retValue = Convert.FromBase64String(retValue.ToString()); } } xmlReader.Read(); } } } } else{ XmlSerializer deserializerWithRootAttribute = ObjectStorage.GetXmlSerializer(DataType, xmlAttrib); retValue = deserializerWithRootAttribute.Deserialize(xmlReader); } return retValue; } override public string ConvertObjectToXml(object value) { if ((value == null) || (value == NullValue))// this case wont happen, this is added in case if code in xml saver changes return String.Empty; Type type = DataType; if (type == typeof(byte[]) || (type == typeof(object) && (value is byte[]))) { return Convert.ToBase64String((byte[])value); } if ((type == typeof(Type)) || ((type == typeof(Object)) && (value is Type))) { return ((Type)value).AssemblyQualifiedName; } if (!IsTypeCustomType(value.GetType())){ // Guid and Type had TypeCode.Object return (string)SqlConvert.ChangeTypeForXML(value, typeof(string)); } if (Type.GetTypeCode(value.GetType()) != TypeCode.Object) { return value.ToString(); } StringWriter strwriter = new StringWriter(FormatProvider); if (implementsIXmlSerializable) { using (XmlTextWriter xmlTextWriter = new XmlTextWriter (strwriter)) { ((IXmlSerializable)value).WriteXml(xmlTextWriter); } return (strwriter.ToString()); } XmlSerializer serializerWithOutRootAttribute = ObjectStorage.GetXmlSerializer(value.GetType()); serializerWithOutRootAttribute.Serialize( strwriter, value); return strwriter.ToString(); } public override void ConvertObjectToXml(object value, XmlWriter xmlWriter, XmlRootAttribute xmlAttrib) { if (null == xmlAttrib) { // implements IXmlSerializable Debug.Assert(xmlWriter is DataTextWriter, "Invalid DataTextWriter is being passed to customer"); ((IXmlSerializable)value).WriteXml(xmlWriter); } else { XmlSerializer serializerWithRootAttribute = ObjectStorage.GetXmlSerializer(value.GetType(), xmlAttrib); serializerWithRootAttribute.Serialize(xmlWriter, value); } } override protected object GetEmptyStorage(int recordCount) { return new Object[recordCount]; } override protected void CopyValue(int record, object store, BitArray nullbits, int storeIndex) { Object[] typedStore = (Object[]) store; typedStore[storeIndex] = values[record]; bool isNull = IsNull(record); nullbits.Set(storeIndex, isNull); if (!isNull && (typedStore[storeIndex] is DateTime)) { DateTime dt = (DateTime)typedStore[storeIndex]; if (dt.Kind == DateTimeKind.Local) { typedStore[storeIndex] = DateTime.SpecifyKind(dt.ToUniversalTime(), DateTimeKind.Local); } } } override protected void SetStorage(object store, BitArray nullbits) { values = (Object[]) store; for(int i = 0; i < values.Length; i++) { if (values[i] is DateTime) { DateTime dt = (DateTime) values[i]; if (dt.Kind == DateTimeKind.Local) { values[i] = (DateTime.SpecifyKind(dt, DateTimeKind.Utc)).ToLocalTime(); } } } // SetNullStorage(nullbits); -> No need to set bits, } // SQLBU 431443: dynamically generated assemblies not cached for XmlSerialization when serializable Udt does not implement IXmlSerializable, "memeory leak" private static readonly object _tempAssemblyCacheLock = new object(); private static Dictionary, XmlSerializer> _tempAssemblyCache; private static readonly XmlSerializerFactory _serializerFactory = new XmlSerializerFactory(); /// /// throw an InvalidOperationException if type implements IDynamicMetaObjectProvider and not IXmlSerializable /// because XmlSerializerFactory will only serialize the type's declared properties, not its dynamic properties /// /// type to test for IDynamicMetaObjectProvider /// DataSet will not serialize types that implement IDynamicMetaObjectProvider but do not also implement IXmlSerializable /// IDynamicMetaObjectProvider was introduced in .Net Framework V4.0 into System.Core internal static void VerifyIDynamicMetaObjectProvider(Type type) { if (typeof(System.Dynamic.IDynamicMetaObjectProvider).IsAssignableFrom(type) && !typeof(System.Xml.Serialization.IXmlSerializable).IsAssignableFrom(type)) { throw ADP.InvalidOperation(Res.GetString(Res.Xml_DynamicWithoutXmlSerializable)); } } internal static XmlSerializer GetXmlSerializer(Type type) { // Dev10 671061: prevent writing an instance which implements IDynamicMetaObjectProvider and not IXmlSerializable // the check here prevents the instance data from being written VerifyIDynamicMetaObjectProvider(type); // use factory which caches XmlSerializer as necessary XmlSerializer serializer = _serializerFactory.CreateSerializer(type); return serializer; } internal static XmlSerializer GetXmlSerializer(Type type, XmlRootAttribute attribute) { XmlSerializer serializer = null; KeyValuePair key = new KeyValuePair(type,attribute); // _tempAssemblyCache is a readonly instance, lock on write to copy & add then replace the original instance. Dictionary, XmlSerializer> cache = _tempAssemblyCache; if ((null == cache) || !cache.TryGetValue(key, out serializer)) { // not in cache, try again with lock because it may need to grow lock(_tempAssemblyCacheLock) { cache = _tempAssemblyCache; if ((null == cache) || !cache.TryGetValue(key, out serializer)) { // Dev10 671061: prevent writing an instance which implements IDynamicMetaObjectProvider and not IXmlSerializable // the check here prevents the instance data from being written VerifyIDynamicMetaObjectProvider(type); // if still not in cache, make cache larger and add new XmlSerializer if (null != cache) { // create larger cache, because dictionary is not reader/writer safe // copy cache so that all readers don't take lock - only potential new writers // same logic used by DbConnectionFactory Dictionary, XmlSerializer> tmp = new Dictionary, XmlSerializer>( 1+cache.Count, TempAssemblyComparer.Default); foreach (KeyValuePair, XmlSerializer> entry in cache) { // copy contents from old cache to new cache tmp.Add(entry.Key, entry.Value); } cache = tmp; } else { // initial creation of cache cache = new Dictionary, XmlSerializer>( TempAssemblyComparer.Default); } // attribute is modifiable - but usuage from XmlSaver & XmlDataLoader & XmlDiffLoader // the instances are not modified, but to be safe - copy the XmlRootAttribute before caching key = new KeyValuePair(type, new XmlRootAttribute()); key.Value.ElementName = attribute.ElementName; key.Value.Namespace = attribute.Namespace; key.Value.DataType = attribute.DataType; key.Value.IsNullable = attribute.IsNullable; serializer = _serializerFactory.CreateSerializer(type, attribute); cache.Add(key, serializer); _tempAssemblyCache = cache; } } } return serializer; } private class TempAssemblyComparer : IEqualityComparer> { internal static readonly IEqualityComparer> Default = new TempAssemblyComparer(); private TempAssemblyComparer() { } public bool Equals(KeyValuePair x, KeyValuePair y) { return ((x.Key == y.Key) && // same type (((x.Value == null) && (y.Value == null)) || // XmlRootAttribute both null ((x.Value != null) && (y.Value != null) && // XmlRootAttribute both with value (x.Value.ElementName == y.Value.ElementName) && // all attribute elements are equal (x.Value.Namespace == y.Value.Namespace) && (x.Value.DataType == y.Value.DataType) && (x.Value.IsNullable == y.Value.IsNullable)))); } public int GetHashCode(KeyValuePair obj) { return unchecked(obj.Key.GetHashCode() + obj.Value.ElementName.GetHashCode()); } } } }