2016-08-03 10:59:49 +00:00
//------------------------------------------------------------------------------
// <copyright file="ObjectStorage.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
2017-08-21 15:34:15 +00:00
// <owner current="true" primary="true">Microsoft</owner>
// <owner current="true" primary="false">Microsoft</owner>
2016-08-03 10:59:49 +00:00
//------------------------------------------------------------------------------
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 < KeyValuePair < Type , XmlRootAttribute > , XmlSerializer > _tempAssemblyCache ;
private static readonly XmlSerializerFactory _serializerFactory = new XmlSerializerFactory ( ) ;
/// <summary>
/// throw an InvalidOperationException if type implements IDynamicMetaObjectProvider and not IXmlSerializable
/// because XmlSerializerFactory will only serialize the type's declared properties, not its dynamic properties
/// </summary>
/// <param name="type">type to test for IDynamicMetaObjectProvider</param>
/// <exception cref="InvalidOperationException">DataSet will not serialize types that implement IDynamicMetaObjectProvider but do not also implement IXmlSerializable</exception>
/// <remarks>IDynamicMetaObjectProvider was introduced in .Net Framework V4.0 into System.Core</remarks>
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 < Type , XmlRootAttribute > key = new KeyValuePair < Type , XmlRootAttribute > ( type , attribute ) ;
// _tempAssemblyCache is a readonly instance, lock on write to copy & add then replace the original instance.
Dictionary < KeyValuePair < Type , XmlRootAttribute > , 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 < KeyValuePair < Type , XmlRootAttribute > , XmlSerializer > tmp =
new Dictionary < KeyValuePair < Type , XmlRootAttribute > , XmlSerializer > (
1 + cache . Count , TempAssemblyComparer . Default ) ;
foreach ( KeyValuePair < KeyValuePair < Type , XmlRootAttribute > , 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 < KeyValuePair < Type , XmlRootAttribute > , 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 , XmlRootAttribute > ( 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 < KeyValuePair < Type , XmlRootAttribute > >
{
internal static readonly IEqualityComparer < KeyValuePair < Type , XmlRootAttribute > > Default = new TempAssemblyComparer ( ) ;
private TempAssemblyComparer ( ) { }
public bool Equals ( KeyValuePair < Type , XmlRootAttribute > x , KeyValuePair < Type , XmlRootAttribute > 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 < Type , XmlRootAttribute > obj )
{
return unchecked ( obj . Key . GetHashCode ( ) + obj . Value . ElementName . GetHashCode ( ) ) ;
}
}
}
}