1 // Licensed to the .NET Foundation under one or more agreements. 2 // The .NET Foundation licenses this file to you under the MIT license. 3 // See the LICENSE file in the project root for more information. 4 5 using System.Data.SqlTypes; 6 using System.Xml; 7 using System.IO; 8 using System.Xml.Serialization; 9 using System.Collections; 10 using System.Collections.Generic; 11 using System.Diagnostics; 12 using System.Runtime.CompilerServices; 13 14 namespace System.Data.Common 15 { 16 internal sealed class SqlUdtStorage : DataStorage 17 { 18 private object[] _values; 19 private readonly bool _implementsIXmlSerializable = false; 20 private readonly bool _implementsIComparable = false; 21 22 private static readonly Dictionary<Type, object> s_typeToNull = new Dictionary<Type, object>(); 23 SqlUdtStorage(DataColumn column, Type type)24 public SqlUdtStorage(DataColumn column, Type type) 25 : this(column, type, GetStaticNullForUdtType(type)) 26 { 27 } 28 SqlUdtStorage(DataColumn column, Type type, object nullValue)29 private SqlUdtStorage(DataColumn column, Type type, object nullValue) 30 : base(column, type, nullValue, nullValue, typeof(ICloneable).IsAssignableFrom(type), GetStorageType(type)) 31 { 32 _implementsIXmlSerializable = typeof(IXmlSerializable).IsAssignableFrom(type); 33 _implementsIComparable = typeof(IComparable).IsAssignableFrom(type); 34 } 35 36 // to support oracle types and other INUllable types that have static Null as field GetStaticNullForUdtType(Type type)37 internal static object GetStaticNullForUdtType(Type type) 38 { 39 object value; 40 if (!s_typeToNull.TryGetValue(type, out value)) 41 { 42 System.Reflection.PropertyInfo propInfo = type.GetProperty("Null", System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Static); 43 if (propInfo != null) 44 value = propInfo.GetValue(null, null); 45 else 46 { 47 System.Reflection.FieldInfo fieldInfo = type.GetField("Null", System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Static); 48 if (fieldInfo != null) 49 { 50 value = fieldInfo.GetValue(null); 51 } 52 else 53 { 54 throw ExceptionBuilder.INullableUDTwithoutStaticNull(type.AssemblyQualifiedName); 55 } 56 } 57 lock (s_typeToNull) 58 { 59 //if(50 < TypeToNull.Count) { 60 // TypeToNull.Clear(); 61 //} 62 s_typeToNull[type] = value; 63 } 64 } 65 return value; 66 } 67 IsNull(int record)68 public override bool IsNull(int record) 69 { 70 return (((INullable)_values[record]).IsNull); 71 } 72 Aggregate(int[] records, AggregateType kind)73 public override object Aggregate(int[] records, AggregateType kind) 74 { 75 throw ExceptionBuilder.AggregateException(kind, _dataType); 76 } 77 Compare(int recordNo1, int recordNo2)78 public override int Compare(int recordNo1, int recordNo2) 79 { 80 return (CompareValueTo(recordNo1, _values[recordNo2])); 81 } 82 CompareValueTo(int recordNo1, object value)83 public override int CompareValueTo(int recordNo1, object value) 84 { 85 if (DBNull.Value == value) 86 { 87 // it is not meaningful compare UDT with DBNull.Value 88 value = _nullValue; 89 } 90 if (_implementsIComparable) 91 { 92 IComparable comparable = (IComparable)_values[recordNo1]; 93 return comparable.CompareTo(value); 94 } 95 else if (_nullValue == value) 96 { 97 INullable nullableValue = (INullable)_values[recordNo1]; 98 return nullableValue.IsNull ? 0 : 1; // left may be null, right is null 99 } 100 101 throw ExceptionBuilder.IComparableNotImplemented(_dataType.AssemblyQualifiedName); 102 } 103 Copy(int recordNo1, int recordNo2)104 public override void Copy(int recordNo1, int recordNo2) 105 { 106 CopyBits(recordNo1, recordNo2); 107 _values[recordNo2] = _values[recordNo1]; 108 } 109 Get(int recordNo)110 public override object Get(int recordNo) 111 { 112 return (_values[recordNo]); 113 } 114 Set(int recordNo, object value)115 public override void Set(int recordNo, object value) 116 { 117 if (DBNull.Value == value) 118 { 119 _values[recordNo] = _nullValue; 120 SetNullBit(recordNo, true); 121 } 122 else if (null == value) 123 { 124 if (_isValueType) 125 { 126 throw ExceptionBuilder.StorageSetFailed(); 127 } 128 else 129 { 130 _values[recordNo] = _nullValue; 131 SetNullBit(recordNo, true); 132 } 133 } 134 else if (!_dataType.IsInstanceOfType(value)) 135 { 136 throw ExceptionBuilder.StorageSetFailed(); 137 } 138 else 139 { 140 // do not clone the value 141 _values[recordNo] = value; 142 SetNullBit(recordNo, false); 143 } 144 } 145 SetCapacity(int capacity)146 public override void SetCapacity(int capacity) 147 { 148 object[] newValues = new object[capacity]; 149 if (_values != null) 150 { 151 Array.Copy(_values, 0, newValues, 0, Math.Min(capacity, _values.Length)); 152 } 153 _values = newValues; 154 base.SetCapacity(capacity); 155 } 156 157 // 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. 158 [MethodImpl(MethodImplOptions.NoInlining)] ConvertXmlToObject(string s)159 public override object ConvertXmlToObject(string s) 160 { 161 if (_implementsIXmlSerializable) 162 { 163 object Obj = System.Activator.CreateInstance(_dataType, true); 164 165 string tempStr = string.Concat("<col>", s, "</col>"); // this is done since you can give fragmet to reader 166 StringReader strReader = new StringReader(tempStr); 167 168 using (XmlTextReader xmlTextReader = new XmlTextReader(strReader)) 169 { 170 ((IXmlSerializable)Obj).ReadXml(xmlTextReader); 171 } 172 return Obj; 173 } 174 175 StringReader strreader = new StringReader(s); 176 XmlSerializer deserializerWithOutRootAttribute = ObjectStorage.GetXmlSerializer(_dataType); 177 return (deserializerWithOutRootAttribute.Deserialize(strreader)); 178 } 179 180 // 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. 181 [MethodImpl(MethodImplOptions.NoInlining)] ConvertXmlToObject(XmlReader xmlReader, XmlRootAttribute xmlAttrib)182 public override object ConvertXmlToObject(XmlReader xmlReader, XmlRootAttribute xmlAttrib) 183 { 184 if (null == xmlAttrib) 185 { 186 string typeName = xmlReader.GetAttribute(Keywords.MSD_INSTANCETYPE, Keywords.MSDNS); 187 if (typeName == null) 188 { 189 string xsdTypeName = xmlReader.GetAttribute(Keywords.MSD_INSTANCETYPE, Keywords.XSINS); // this xsd type 190 if (null != xsdTypeName) 191 { 192 typeName = XSDSchema.XsdtoClr(xsdTypeName).FullName; 193 } 194 } 195 Type type = (typeName == null) ? _dataType : Type.GetType(typeName); 196 object Obj = System.Activator.CreateInstance(type, true); 197 Debug.Assert(xmlReader is DataTextReader, "Invalid DataTextReader is being passed to customer"); 198 ((IXmlSerializable)Obj).ReadXml(xmlReader); 199 return Obj; 200 } 201 else 202 { 203 XmlSerializer deserializerWithRootAttribute = ObjectStorage.GetXmlSerializer(_dataType, xmlAttrib); 204 return (deserializerWithRootAttribute.Deserialize(xmlReader)); 205 } 206 } 207 208 ConvertObjectToXml(object value)209 public override string ConvertObjectToXml(object value) 210 { 211 StringWriter strwriter = new StringWriter(FormatProvider); 212 if (_implementsIXmlSerializable) 213 { 214 using (XmlTextWriter xmlTextWriter = new XmlTextWriter(strwriter)) 215 { 216 ((IXmlSerializable)value).WriteXml(xmlTextWriter); 217 } 218 } 219 else 220 { 221 XmlSerializer serializerWithOutRootAttribute = ObjectStorage.GetXmlSerializer(value.GetType()); 222 serializerWithOutRootAttribute.Serialize(strwriter, value); 223 } 224 return (strwriter.ToString()); 225 } 226 ConvertObjectToXml(object value, XmlWriter xmlWriter, XmlRootAttribute xmlAttrib)227 public override void ConvertObjectToXml(object value, XmlWriter xmlWriter, XmlRootAttribute xmlAttrib) 228 { 229 if (null == xmlAttrib) 230 { 231 Debug.Assert(xmlWriter is DataTextWriter, "Invalid DataTextWriter is being passed to customer"); 232 ((IXmlSerializable)value).WriteXml(xmlWriter); 233 } 234 else 235 { 236 // we support polymorphism only for types that implements IXmlSerializable. 237 // Assumption: value is the same type as DataType 238 239 XmlSerializer serializerWithRootAttribute = ObjectStorage.GetXmlSerializer(_dataType, xmlAttrib); 240 serializerWithRootAttribute.Serialize(xmlWriter, value); 241 } 242 } 243 GetEmptyStorage(int recordCount)244 protected override object GetEmptyStorage(int recordCount) 245 { 246 return new object[recordCount]; 247 } 248 CopyValue(int record, object store, BitArray nullbits, int storeIndex)249 protected override void CopyValue(int record, object store, BitArray nullbits, int storeIndex) 250 { 251 object[] typedStore = (object[])store; 252 typedStore[storeIndex] = _values[record]; 253 nullbits.Set(storeIndex, IsNull(record)); 254 } 255 SetStorage(object store, BitArray nullbits)256 protected override void SetStorage(object store, BitArray nullbits) 257 { 258 _values = (object[])store; 259 //SetNullStorage(nullbits); 260 } 261 } 262 } 263