486 lines · 19.8 KB · C#
RawDownload
//******************************************************************************************************//  DataSetExtensions.cs - Gbtc////  Copyright © 2013, Grid Protection Alliance.  All Rights Reserved.////  Licensed to the Grid Protection Alliance (GPA) under one or more contributor license agreements. See//  the NOTICE file distributed with this work for additional information regarding copyright ownership.//  The GPA licenses this file to you under the MIT License (MIT), the "License"; you may//  not use this file except in compliance with the License. You may obtain a copy of the License at:////      http://www.opensource.org/licenses/MIT////  Unless agreed to in writing, the subject software distributed under the License is distributed on an//  "AS-IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. Refer to the//  License for the specific language governing permissions and limitations.////  Code Modification History://  ----------------------------------------------------------------------------------------------------//  02/07/2013 - J. Ritchie Carroll//       Generated original version of source code.////****************************************************************************************************** using System;using System.Collections.Generic;using System.Data;using System.Diagnostics.CodeAnalysis;using System.IO;using GSF.IO; namespace GSF.Data{    /// <summary>    /// Data types available to a <see cref="DataSet"/> object.    /// </summary>    public enum DataType : byte    {        /// <summary>        /// Boolean data type, <see cref="Boolean"/>.        /// </summary>        Boolean,        /// <summary>        /// Unsigned 8-bit byte data type, <see cref="Byte"/>.        /// </summary>        Byte,        /// <summary>        /// 16-bit character data type, <see cref="Char"/>.        /// </summary>        Char,        /// <summary>        /// Date/time data type, <see cref="DateTime"/>.        /// </summary>        DateTime,        /// <summary>        /// Decimal data type, <see cref="Decimal"/>.        /// </summary>        Decimal,        /// <summary>        /// 64-bit double precision floating point numeric data type, <see cref="Double"/>.        /// </summary>        Double,        /// <summary>        /// Unsigned 128-bit Guid integer data type, <see cref="Guid"/>.        /// </summary>        Guid,        /// <summary>        /// Signed 16-bit integer data type, <see cref="Int16"/>.        /// </summary>        Int16,        /// <summary>        /// Signed 32-bit integer data type, <see cref="Int32"/>.        /// </summary>        Int32,        /// <summary>        /// Signed 64-bit integer data type, <see cref="Int64"/>        /// </summary>        Int64,        /// <summary>        /// Signed byte data type, <see cref="SByte"/>.        /// </summary>        SByte,        /// <summary>        /// 32-bit single precision floating point numeric data type, <see cref="Single"/>.        /// </summary>        Single,        /// <summary>        /// Character array data type, <see cref="String"/>.        /// </summary>        String,        /// <summary>        /// Time-span data type, <see cref="TimeSpan"/>.        /// </summary>        TimeSpan,        /// <summary>        /// Unsigned 16-bit integer data type, <see cref="UInt16"/>.        /// </summary>        UInt16,        /// <summary>        /// Unsigned 32-bit integer data type, <see cref="UInt32"/>.        /// </summary>        UInt32,        /// <summary>        /// Unsigned 64-bit integer data type, <see cref="UInt64"/>.        /// </summary>        UInt64,        /// <summary>        /// Unsigned byte array data type.        /// </summary>        Blob,        /// <summary>        /// User defined/other data type.        /// </summary>        Object    }     /// <summary>    /// Defines extension functions related to <see cref="DataSet"/> instances.    /// </summary>    public static class DataSetExtensions    {        // Constant array of supported data types        private static readonly Type[] s_supportedDataTypes =        {            // This must match DataType enum order            typeof(bool),            typeof(byte),            typeof(char),            typeof(DateTime),            typeof(decimal),            typeof(double),            typeof(Guid),            typeof(short),            typeof(int),            typeof(long),            typeof(sbyte),            typeof(float),            typeof(string),            typeof(TimeSpan),            typeof(ushort),            typeof(uint),            typeof(ulong),            typeof(byte[]),            typeof(object)        };         /// <summary>        /// Serializes a <see cref="DataSet"/> to a destination <see cref="Stream"/>.        /// </summary>        /// <param name="source"><see cref="DataSet"/> to serialize.</param>        /// <param name="destination"><see cref="Stream"/> to serialize <see cref="DataSet"/> on.</param>        /// <param name="assumeStringForUnknownTypes">Flag to determine if unknown column types should be serialized as strings.</param>        /// <param name="useNullableDataTypes">Flag to determine if extra information should be serialized to support null values.</param>        [SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")]        public static void SerializeToStream(this DataSet source, Stream destination, bool assumeStringForUnknownTypes = true, bool useNullableDataTypes = true)        {            if (source is null)                throw new ArgumentNullException(nameof(source));             if (destination is null)                throw new ArgumentNullException(nameof(destination));             if (!destination.CanWrite)                throw new InvalidOperationException("Cannot write to a read-only stream");             BinaryWriter output = new BinaryWriter(destination);             // Serialize dataset name and table count            output.Write(source.DataSetName);            output.Write(source.Tables.Count);             // Serialize tables            foreach (DataTable table in source.Tables)            {                List<int> columnIndices = new List<int>();                List<DataType> columnDataTypes = new List<DataType>();                 // Serialize column metadata                using (BlockAllocatedMemoryStream columnMetaDataStream = new BlockAllocatedMemoryStream())                {                    BinaryWriter columnMetaData = new BinaryWriter(columnMetaDataStream);                     foreach (DataColumn column in table.Columns)                    {                        // Get column data type, unknown types will be represented as object                        DataType dataType = GetDataType(column.DataType, assumeStringForUnknownTypes);                         // Only objects of a known type can be properly serialized                        if (dataType != DataType.Object)                        {                            byte dtByte = (byte)dataType;                             if (useNullableDataTypes)                                dtByte |= 0x80;                             // Serialize column name and type                            columnMetaData.Write(column.ColumnName);                            columnMetaData.Write(dtByte);                             // Track data types and column indices in parallel lists for faster DataRow serialization                            columnIndices.Add(column.Ordinal);                            columnDataTypes.Add(dataType);                        }                    }                     // Serialize table name and column count                    output.Write(table.TableName);                    output.Write(columnIndices.Count);                     // Write column metadata                    output.Write(columnMetaDataStream.ToArray(), 0, (int)columnMetaDataStream.Length);                }                 // Serialize row count                output.Write(table.Rows.Count);                 // Serialize rows                foreach (DataRow row in table.Rows)                {                    // Serialize column data                    for (int i = 0; i < columnIndices.Count; i++)                    {                        object value = row[columnIndices[i]];                         if (useNullableDataTypes)                        {                            output.Write((byte)(value == DBNull.Value ? 1 : 0));                             if (value == DBNull.Value)                                continue;                        }                         switch (columnDataTypes[i])                        {                            case DataType.Boolean:                                output.Write(value.NotDBNull<bool>());                                break;                            case DataType.Byte:                                output.Write(value.NotDBNull<byte>());                                break;                            case DataType.Char:                                output.Write(value.NotDBNull<char>());                                break;                            case DataType.DateTime:                                output.Write(value.NotDBNull<DateTime>().Ticks);                                break;                            case DataType.Decimal:                                output.Write(value.NotDBNull<decimal>());                                break;                            case DataType.Double:                                output.Write(value.NotDBNull<double>());                                break;                            case DataType.Guid:                                output.Write(value.NotDBNull<Guid>().ToByteArray());                                break;                            case DataType.Int16:                                output.Write(value.NotDBNull<short>());                                break;                            case DataType.Int32:                                output.Write(value.NotDBNull<int>());                                break;                            case DataType.Int64:                                output.Write(value.NotDBNull<long>());                                break;                            case DataType.SByte:                                output.Write(value.NotDBNull<sbyte>());                                break;                            case DataType.Single:                                output.Write(value.NotDBNull<float>());                                break;                            case DataType.String:                                output.Write(value.NotDBNullString());                                break;                            case DataType.TimeSpan:                                output.Write(value.NotDBNull<TimeSpan>().Ticks);                                break;                            case DataType.UInt16:                                output.Write(value.NotDBNull<ushort>());                                break;                            case DataType.UInt32:                                output.Write(value.NotDBNull<uint>());                                break;                            case DataType.UInt64:                                output.Write(value.NotDBNull<ulong>());                                break;                            case DataType.Blob:                                byte[] blob = value.NotDBNull<byte[]>();                                 if (blob is null || blob.Length == 0)                                {                                    output.Write(0);                                }                                else                                {                                    output.Write(blob.Length);                                    output.Write(blob);                                }                                 break;                        }                    }                }            }        }         /// <summary>        /// Deserializes a <see cref="DataSet"/> from a <see cref="Stream"/>.        /// </summary>        /// <param name="source"><see cref="Stream"/> to deserialize <see cref="DataSet"/> from.</param>        [SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")]        public static DataSet DeserializeToDataSet(this Stream source)        {            if (source is null)                throw new ArgumentNullException(nameof(source));             if (!source.CanRead)                throw new InvalidOperationException("Cannot read from a write-only stream");             DataSet dataset = new DataSet();            BinaryReader input = new BinaryReader(source);             // Deserialize dataset name and table count            dataset.DataSetName = input.ReadString();            int tableCount = input.ReadInt32();             // Deserialize tables            for (int i = 0; i < tableCount; i++)            {                List<int> columnIndices = new List<int>();                List<DataType> columnDataTypes = new List<DataType>();                List<bool> columnNullable = new List<bool>();                 DataTable table = dataset.Tables.Add();                 // Deserialize table name and column count                table.TableName = input.ReadString();                int columnCount = input.ReadInt32();                 // Deserialize column metadata                for (int j = 0; j < columnCount; j++)                {                    DataColumn column = table.Columns.Add();                     // Deserialize column name and type                    column.ColumnName = input.ReadString();                     byte dtByte = input.ReadByte();                    DataType dataType = (DataType)(dtByte & 0x7F);                    column.DataType = dataType.DeriveColumnType();                    columnNullable.Add((dtByte & 0x80) != 0);                     // Track data types and column indices in parallel lists for faster DataRow deserialization                    columnIndices.Add(column.Ordinal);                    columnDataTypes.Add(dataType);                }                 // Deserialize row count                int rowCount = input.ReadInt32();                 // Deserialize rows                for (int j = 0; j < rowCount; j++)                {                    DataRow row = table.NewRow();                     // Deserialize column data                    for (int k = 0; k < columnIndices.Count; k++)                    {                        object value = null;                         if (columnNullable[k] && input.ReadByte() != 0)                        {                            // Set column value to DBNull                            row[columnIndices[k]] = DBNull.Value;                            continue;                        }                         switch (columnDataTypes[k])                        {                            case DataType.Boolean:                                value = input.ReadBoolean();                                break;                            case DataType.Byte:                                value = input.ReadByte();                                break;                            case DataType.Char:                                value = input.ReadChar();                                break;                            case DataType.DateTime:                                value = new DateTime(input.ReadInt64());                                break;                            case DataType.Decimal:                                value = input.ReadDecimal();                                break;                            case DataType.Double:                                value = input.ReadDouble();                                break;                            case DataType.Guid:                                value = new Guid(input.ReadBytes(16));                                break;                            case DataType.Int16:                                value = input.ReadInt16();                                break;                            case DataType.Int32:                                value = input.ReadInt32();                                break;                            case DataType.Int64:                                value = input.ReadInt64();                                break;                            case DataType.SByte:                                value = input.ReadSByte();                                break;                            case DataType.Single:                                value = input.ReadSingle();                                break;                            case DataType.String:                                value = input.ReadString();                                break;                            case DataType.TimeSpan:                                value = new TimeSpan(input.ReadInt64());                                break;                            case DataType.UInt16:                                value = input.ReadUInt16();                                break;                            case DataType.UInt32:                                value = input.ReadUInt32();                                break;                            case DataType.UInt64:                                value = input.ReadUInt64();                                break;                            case DataType.Blob:                                int byteCount = input.ReadInt32();                                 if (byteCount > 0)                                    value = input.ReadBytes(byteCount);                                 break;                        }                         // Update column value                        row[columnIndices[k]] = value;                    }                     // Add new row to table                    table.Rows.Add(row);                }            }             return dataset;        }         /// <summary>        /// Attempts to derive <see cref="DataType"/> based on object <see cref="Type"/>.        /// </summary>        /// <param name="objectType"><see cref="Type"/> of object to test.</param>        /// <returns>Derived <see cref="DataType"/> based on object <see cref="Type"/> if matched; otherwise <see cref="DataType.Object"/>.</returns>        /// <param name="assumeStringForUnknownTypes">Flag to determine if unknown column types should be serialized as strings.</param>        public static DataType GetDataType(this Type objectType, bool assumeStringForUnknownTypes = true)        {            DataType dataType = DataType.Object;             for (int i = 0; i < s_supportedDataTypes.Length; i++)            {                if (objectType == s_supportedDataTypes[i])                    dataType = (DataType)i;            }             return assumeStringForUnknownTypes ? (dataType == DataType.Object ? DataType.String : dataType) : dataType;        }         /// <summary>        /// Gets column object <see cref="Type"/> from given <see cref="DataType"/>.        /// </summary>        /// <param name="dataType"><see cref="DataType"/> to derive object <see cref="Type"/> from.</param>        /// <returns>Object <see cref="Type"/> derived from given <see cref="DataType"/>.</returns>        public static Type DeriveColumnType(this DataType dataType) =>             s_supportedDataTypes[(int)dataType];         private static string NotDBNullString(this object value) =>             value == DBNull.Value ? "" : value.ToString();         private static T NotDBNull<T>(this object value, T defaultValue) =>             value == DBNull.Value ? defaultValue : (T)value;         private static T NotDBNull<T>(this object value) =>             value.NotDBNull(default(T));    }}

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t