674 lines
26 KiB
C#
674 lines
26 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Collections.ObjectModel;
|
|
using System.Data.Common;
|
|
using System.Linq;
|
|
using Sarand.Net.Standard.Base.Core;
|
|
using Sarand.Net.Standard.Base.Dbas;
|
|
using Sarand.Net.Standard.Dbas.Base;
|
|
using Microsoft.Data.SqlClient;
|
|
using Sarand.Net.Standard.Base.Utils;
|
|
using Sarand.Net.Standard.Base.App;
|
|
|
|
namespace Sarand.Net.Standard.Dbas.Data
|
|
{
|
|
//=======================================================================
|
|
public abstract class DbDataItem : IDbDataItem
|
|
{
|
|
//-------------------------------------------------------------------
|
|
// D a t a b a s e I n f o
|
|
//-------------------------------------------------------------------
|
|
// Definitions.
|
|
/// <summary>
|
|
/// Database information - object supplied by main application.
|
|
/// <para>NB Application may have multiple databases with schema differences.</para>
|
|
/// <para>Each instance of a DbDataItem must be database specific.</para>
|
|
/// </summary>
|
|
public class DatabaseInfo : IDatabaseInfo
|
|
{
|
|
// Data members.
|
|
private readonly List<DbDfnTable> tables;
|
|
private static readonly String defaultSchemaName = "dbo";
|
|
// Construction.
|
|
public DatabaseInfo(ISacsDatabase db)
|
|
{
|
|
this.DatabaseName = db.DatabaseName;
|
|
this.tables = db.GetTableDfns();
|
|
}
|
|
// Properties.
|
|
public String DatabaseName { get; }
|
|
public String Context { get { return StringUtil.OnEmpty(DatabaseName, "UnknownDb"); } }
|
|
// Database schema.
|
|
public DbDfnTable GetTableDefinition(String schemaName, String tableName)
|
|
{
|
|
foreach (DbDfnTable table in tables.FindAll(item => item.Name.EqualsCI(tableName)))
|
|
{
|
|
if (StringUtil.OnEmpty(table.Schema, defaultSchemaName).EqualsCI(StringUtil.OnEmpty(schemaName, defaultSchemaName)))
|
|
return table;
|
|
}
|
|
return null;
|
|
}
|
|
public bool TableExists(String schemaName, String tableName)
|
|
{
|
|
return GetTableDefinition(schemaName, tableName) != null;
|
|
}
|
|
public bool ColumnExists(String schemaName, String tableName, String columnName)
|
|
{
|
|
DbDfnTable table = GetTableDefinition(schemaName, tableName);
|
|
if (table == null) return false;
|
|
return table.HasColumnByName(columnName);
|
|
}
|
|
public List<DbDfnColumn> GetColumnsForTable(String schemaName, String tableName)
|
|
{
|
|
DbDfnTable table = GetTableDefinition(schemaName, tableName);
|
|
if (table != null) return table.Columns;
|
|
return new List<DbDfnColumn>();
|
|
}
|
|
}
|
|
|
|
//-------------------------------------------------------------------
|
|
// C o l u m n D a t a
|
|
//-------------------------------------------------------------------
|
|
/// <summary>Column data - column definition for DbDataItem.</summary>
|
|
public class ColumnData
|
|
{
|
|
protected ColumnData(String name, bool identity = false)
|
|
{
|
|
Name = name;
|
|
Identity = identity;
|
|
}
|
|
public String Name { get; }
|
|
public bool Identity { get; }
|
|
}
|
|
public class ColumnDataString : ColumnData
|
|
{
|
|
public ColumnDataString(String name, bool identity = false) : base(name, identity) { }
|
|
public String Value { get; set; } = String.Empty;
|
|
}
|
|
public class ColumnDataBoolean : ColumnData
|
|
{
|
|
public ColumnDataBoolean(String name, bool identity = false) : base(name, identity) { }
|
|
public Boolean Value { get; set; } = false;
|
|
}
|
|
public class ColumnDataBooleanNullable : ColumnData
|
|
{
|
|
public ColumnDataBooleanNullable(String name, bool identity = false) : base(name, identity) { }
|
|
public Boolean? Value { get; set; } = null;
|
|
}
|
|
public class ColumnDataByte : ColumnData
|
|
{
|
|
public ColumnDataByte(String name, bool identity = false) : base(name, identity) { }
|
|
public Byte Value { get; set; } = 0;
|
|
}
|
|
public class ColumnDataByteNullable : ColumnData
|
|
{
|
|
public ColumnDataByteNullable(String name, bool identity = false) : base(name, identity) { }
|
|
public Byte? Value { get; set; } = null;
|
|
}
|
|
public class ColumnDataInt16 : ColumnData
|
|
{
|
|
public ColumnDataInt16(String name, bool identity = false) : base(name, identity) { }
|
|
public Int16 Value { get; set; } = 0;
|
|
}
|
|
public class ColumnDataInt16Nullable : ColumnData
|
|
{
|
|
public ColumnDataInt16Nullable(String name, bool identity = false) : base(name, identity) { }
|
|
public Int16? Value { get; set; } = null;
|
|
}
|
|
public class ColumnDataInt32 : ColumnData
|
|
{
|
|
public ColumnDataInt32(String name, bool identity = false) : base(name, identity) { }
|
|
public Int32 Value { get; set; } = 0;
|
|
}
|
|
public class ColumnDataInt32Nullable : ColumnData
|
|
{
|
|
public ColumnDataInt32Nullable(String name, bool identity = false) : base(name, identity) { }
|
|
public Int32? Value { get; set; } = null;
|
|
}
|
|
public class ColumnDataInt64 : ColumnData
|
|
{
|
|
public ColumnDataInt64(String name, bool identity = false) : base(name, identity) { }
|
|
public Int64 Value { get; set; } = 0;
|
|
}
|
|
public class ColumnDataInt64Nullable : ColumnData
|
|
{
|
|
public ColumnDataInt64Nullable(String name, bool identity = false) : base(name, identity) { }
|
|
public Int64? Value { get; set; } = null;
|
|
}
|
|
public class ColumnDataSingle : ColumnData
|
|
{
|
|
public ColumnDataSingle(String name, bool identity = false) : base(name, identity) { }
|
|
public Single Value { get; set; } = 0;
|
|
}
|
|
public class ColumnDataSingleNullable : ColumnData
|
|
{
|
|
public ColumnDataSingleNullable(String name, bool identity = false) : base(name, identity) { }
|
|
public Single? Value { get; set; } = null;
|
|
}
|
|
public class ColumnDataDouble : ColumnData
|
|
{
|
|
public ColumnDataDouble(String name, bool identity = false) : base(name, identity) { }
|
|
public Double Value { get; set; } = 0;
|
|
}
|
|
public class ColumnDataDecimal : ColumnData
|
|
{
|
|
public ColumnDataDecimal(String name, bool identity = false) : base(name, identity) { }
|
|
public Decimal Value { get; set; } = 0;
|
|
}
|
|
public class ColumnDataDecimalNullable : ColumnData
|
|
{
|
|
public ColumnDataDecimalNullable(String name, bool identity = false) : base(name, identity) { }
|
|
public Decimal? Value { get; set; } = null;
|
|
}
|
|
public class ColumnDataDateTime : ColumnData
|
|
{
|
|
public ColumnDataDateTime(String name, bool identity = false) : base(name, identity) { }
|
|
public DateTime Value { get; set; } = DateTime.MinValue;
|
|
}
|
|
public class ColumnDataDateTimeNullable : ColumnData
|
|
{
|
|
public ColumnDataDateTimeNullable(String name, bool identity = false) : base(name, identity) { }
|
|
public DateTime? Value { get; set; } = null;
|
|
}
|
|
public class ColumnDataGuid : ColumnData
|
|
{
|
|
public ColumnDataGuid(String name, bool identity = false) : base(name, identity) { }
|
|
public Guid Value { get; set; } = default(Guid);
|
|
}
|
|
public class ColumnDataGuidNullable : ColumnData
|
|
{
|
|
public ColumnDataGuidNullable(String name, bool identity = false) : base(name, identity) { }
|
|
public Guid Value { get; set; } = default(Guid);
|
|
}
|
|
public class ColumnDataByteArray : ColumnData
|
|
{
|
|
public ColumnDataByteArray(String name, bool identity = false) : base(name, identity) { }
|
|
public Byte[] Value { get; set; } = null;
|
|
}
|
|
public class ColumnDataByteArrayNullable : ColumnData
|
|
{
|
|
public ColumnDataByteArrayNullable(String name, bool identity = false) : base(name, identity) { }
|
|
public Byte[] Value { get; set; } = null;
|
|
}
|
|
|
|
//-------------------------------------------------------------------
|
|
// D b D a t a I t e m
|
|
//-------------------------------------------------------------------
|
|
// Data members.
|
|
private List<String> existingColumnNamesFull = null;
|
|
|
|
//-------------------------------------------------------------------
|
|
// Construction.
|
|
protected DbDataItem(IDatabaseInfo dbInfo = null)
|
|
{
|
|
DbInfo = dbInfo;
|
|
}
|
|
|
|
//-------------------------------------------------------------------
|
|
// Properties.
|
|
protected IDatabaseInfo DbInfo { get; }
|
|
public abstract String SchemaName { get; }
|
|
public abstract String TableName { get; }
|
|
public virtual List<String> AlternativeTableNames { get; } = new List<string>();
|
|
public static List<String> MissingColumns { get { columnsMissing.Sort(); return new List<string>(columnsMissing); } }
|
|
|
|
//-------------------------------------------------------------------
|
|
/// <summary>Determine the table name used on the database which can differ if alternative names are available.</summary>
|
|
protected String GetActualTableName(String tableName)
|
|
{
|
|
if (DbInfo != null)
|
|
{
|
|
if (DbInfo.TableExists(SchemaName, tableName)) return tableName;
|
|
foreach (String tableNameAlternate in AlternativeTableNames)
|
|
if (DbInfo.TableExists(SchemaName, tableNameAlternate)) return tableNameAlternate;
|
|
}
|
|
return tableName;
|
|
}
|
|
|
|
//-------------------------------------------------------------------
|
|
// Population.
|
|
public abstract void PopulateFrom(DbCommand dbCommand, String tableAlias, String filter);
|
|
public abstract void PopulateFrom(DbDataReader dbdataReader);
|
|
|
|
//-------------------------------------------------------------------
|
|
// Database schema.
|
|
public bool ColumnExists(String columnName)
|
|
{
|
|
if (DbInfo != null)
|
|
return DbInfo.ColumnExists(SchemaName, TableName, columnName);
|
|
return true;
|
|
}
|
|
public List<String> GetExistingColumnNames(List<String> columnNames)
|
|
{
|
|
List<String> columnNamesExisting = new List<string>(columnNames);
|
|
if (existingColumnNamesFull == null)
|
|
{
|
|
// 'Existing column names (full)' contains the full list of columns confirmed to be on the database.
|
|
// NB The list will be empty if no DbInfo supplied or the table is not found on the database.
|
|
if (DbInfo != null)
|
|
{
|
|
List<DbDfnColumn> existingColumns = DbInfo.GetColumnsForTable(SchemaName, TableName);
|
|
existingColumnNamesFull = existingColumns.GetNames();
|
|
}
|
|
else existingColumnNamesFull = new List<string>();
|
|
}
|
|
if (existingColumnNamesFull.Count > 0)
|
|
{
|
|
// Remove columns that don't exist on the table.
|
|
columnNamesExisting.RemoveAll(item => !existingColumnNamesFull.ContainsCI(item));
|
|
}
|
|
return columnNamesExisting;
|
|
}
|
|
|
|
//-------------------------------------------------------------------
|
|
// Data Reader interface.
|
|
/// <summary>
|
|
/// Get column index by name.
|
|
/// <para>Returns -1 if not found and logs message.</para>
|
|
/// </summary>
|
|
protected int GetColumnOrdinal(DbDataReader dbDataReader, String columnName)
|
|
{
|
|
// Identify whether the specified column name is in the list of columns in the results set.
|
|
// NB Efficient as doesn't load the table schema and avoids logging of exception.
|
|
// Ref. https://stackoverflow.com/questions/373230/check-for-column-name-in-a-sqldatareader-object
|
|
bool columnExists = Enumerable.Range(0, dbDataReader.FieldCount).Any(i => string.Equals(dbDataReader.GetName(i), columnName, StringComparison.OrdinalIgnoreCase));
|
|
if (columnExists)
|
|
return dbDataReader.GetOrdinal(columnName);
|
|
// Invalid column name for result set.
|
|
if (DbInfo != null && !ColumnExists(columnName))
|
|
LogMissingColumn(columnName);
|
|
else SaLogger.Warn($"{SysUtil.Fn} Column not found in result set: '{columnName}'");
|
|
return -1;
|
|
|
|
/*
|
|
* Dec-2018 Previous logic trapping specific exception.
|
|
* Works well but automatically logs exception to debug console.
|
|
try
|
|
{
|
|
return dbDataReader.GetOrdinal(columnName);
|
|
}
|
|
catch (Exception exc)
|
|
{
|
|
if (exc.GetType().Name.EqualsCI("IndexOutOfRangeException"))
|
|
{
|
|
// Invalid column name for result set.
|
|
if (DbInfo != null && !ColumnExists(columnName))
|
|
{
|
|
String columnIdentifier = DbInfo.Context+"."+(!StringUtil.IsEmpty(SchemaName)?SchemaName+".":"")+TableName+"."+columnName;
|
|
if (!missingColumns.ContainsCI(columnIdentifier))
|
|
{
|
|
missingColumns.Add(columnIdentifier);
|
|
// Message is only logged on first occurence.
|
|
SaLogger.Debug($"{SysUtil.Fn} Column not found on database: {columnIdentifier}");
|
|
}
|
|
}
|
|
else SaLogger.Warn($"{SysUtil.Fn} Column not found in result set: '{columnName}'");
|
|
return -1;
|
|
}
|
|
}
|
|
return -1;
|
|
*/
|
|
}
|
|
|
|
//-------------------------------------------------------------------
|
|
// Retrieve field values.
|
|
|
|
public String GetFieldString(DbDataReader dbDataReader, String columnName, String defaultValue)
|
|
{
|
|
int columnIdx = GetColumnOrdinal(dbDataReader, columnName);
|
|
if (columnIdx < 0) return defaultValue;
|
|
// Check for null value (to avoid throwing exception).
|
|
if (!dbDataReader.IsDBNull(columnIdx))
|
|
{
|
|
Type dataType = dbDataReader.GetFieldType(columnIdx);
|
|
// Expected data type.
|
|
if (dataType == typeof(String))
|
|
return dbDataReader.GetString(columnIdx);
|
|
LogAlternativeTypeColumn(columnName, typeof(String), dataType);
|
|
}
|
|
return defaultValue;
|
|
}
|
|
|
|
public Boolean GetFieldBoolean(DbDataReader dbDataReader, String columnName, Boolean defaultValue)
|
|
{
|
|
return GetFieldBooleanNullable(dbDataReader, columnName, defaultValue) ?? defaultValue;
|
|
}
|
|
|
|
public Boolean? GetFieldBooleanNullable(DbDataReader dbDataReader, String columnName, Boolean? defaultValue)
|
|
{
|
|
int columnIdx = GetColumnOrdinal(dbDataReader, columnName);
|
|
if (columnIdx < 0) return defaultValue;
|
|
// Check for null value (to avoid throwing exception).
|
|
if (!dbDataReader.IsDBNull(columnIdx))
|
|
{
|
|
Type dataType = dbDataReader.GetFieldType(columnIdx);
|
|
// Expected data type.
|
|
if (dataType == typeof(Boolean))
|
|
return dbDataReader.GetBoolean(columnIdx);
|
|
LogAlternativeTypeColumn(columnName, typeof(Boolean), dataType);
|
|
}
|
|
return defaultValue;
|
|
}
|
|
|
|
public Byte GetFieldByte(DbDataReader dbDataReader, String columnName, Byte defaultValue)
|
|
{
|
|
return GetFieldByteNullable(dbDataReader, columnName, defaultValue) ?? defaultValue;
|
|
}
|
|
|
|
public Byte? GetFieldByteNullable(DbDataReader dbDataReader, String columnName, Byte? defaultValue)
|
|
{
|
|
int columnIdx = GetColumnOrdinal(dbDataReader, columnName);
|
|
if (columnIdx < 0) return defaultValue;
|
|
// Check for null value (to avoid throwing exception).
|
|
if (!dbDataReader.IsDBNull(columnIdx))
|
|
{
|
|
Type dataType = dbDataReader.GetFieldType(columnIdx);
|
|
// Expected data type.
|
|
if (dataType == typeof(Byte))
|
|
return dbDataReader.GetByte(columnIdx);
|
|
LogAlternativeTypeColumn(columnName, typeof(Byte), dataType);
|
|
}
|
|
return defaultValue;
|
|
}
|
|
|
|
public Int16 GetFieldInt16(DbDataReader dbDataReader, String columnName, Int16 defaultValue)
|
|
{
|
|
return GetFieldInt16Nullable(dbDataReader, columnName, defaultValue) ?? defaultValue;
|
|
}
|
|
|
|
public Int16? GetFieldInt16Nullable(DbDataReader dbDataReader, String columnName, Int16? defaultValue)
|
|
{
|
|
int columnIdx = GetColumnOrdinal(dbDataReader, columnName);
|
|
if (columnIdx < 0) return defaultValue;
|
|
// Check for null value (to avoid throwing exception).
|
|
if (!dbDataReader.IsDBNull(columnIdx))
|
|
{
|
|
Type dataType = dbDataReader.GetFieldType(columnIdx);
|
|
// Expected data type.
|
|
if (dataType == typeof(Int16))
|
|
return dbDataReader.GetInt16(columnIdx);
|
|
LogAlternativeTypeColumn(columnName, typeof(Int16), dataType);
|
|
}
|
|
return defaultValue;
|
|
}
|
|
|
|
public Int32 GetFieldInt32(DbDataReader dbDataReader, String columnName, Int32 defaultValue)
|
|
{
|
|
return GetFieldInt32Nullable(dbDataReader, columnName, defaultValue) ?? defaultValue;
|
|
}
|
|
|
|
public Int32? GetFieldInt32Nullable(DbDataReader dbDataReader, String columnName, Int32? defaultValue)
|
|
{
|
|
int columnIdx = GetColumnOrdinal(dbDataReader, columnName);
|
|
if (columnIdx < 0) return defaultValue;
|
|
// Check for null value (to avoid throwing exception).
|
|
if (!dbDataReader.IsDBNull(columnIdx))
|
|
{
|
|
Type dataType = dbDataReader.GetFieldType(columnIdx);
|
|
// Expected data type.
|
|
if (dataType == typeof(Int32))
|
|
return dbDataReader.GetInt32(columnIdx);
|
|
LogAlternativeTypeColumn(columnName, typeof(Int32), dataType);
|
|
if (dataType == typeof(Int16))
|
|
return (Int32)dbDataReader.GetInt16(columnIdx);
|
|
}
|
|
return defaultValue;
|
|
}
|
|
|
|
public Int64 GetFieldInt64(DbDataReader dbDataReader, String columnName, Int64 defaultValue)
|
|
{
|
|
return GetFieldInt64Nullable(dbDataReader, columnName, defaultValue) ?? defaultValue;
|
|
}
|
|
|
|
public Int64? GetFieldInt64Nullable(DbDataReader dbDataReader, String columnName, Int64? defaultValue)
|
|
{
|
|
int columnIdx = GetColumnOrdinal(dbDataReader, columnName);
|
|
if (columnIdx < 0) return defaultValue;
|
|
// Check for null value (to avoid throwing exception).
|
|
if (!dbDataReader.IsDBNull(columnIdx))
|
|
{
|
|
Type dataType = dbDataReader.GetFieldType(columnIdx);
|
|
// Expected data type.
|
|
if (dataType == typeof(Int64))
|
|
return dbDataReader.GetInt64(columnIdx);
|
|
LogAlternativeTypeColumn(columnName, typeof(Int64), dataType);
|
|
if (dataType == typeof(Int32))
|
|
return (Int64)dbDataReader.GetInt32(columnIdx);
|
|
if (dataType == typeof(Int16))
|
|
return (Int64)dbDataReader.GetInt16(columnIdx);
|
|
}
|
|
return defaultValue;
|
|
}
|
|
|
|
public Single GetFieldSingle(DbDataReader dbDataReader, String columnName, Single defaultValue)
|
|
{
|
|
return GetFieldSingleNullable(dbDataReader, columnName, defaultValue) ?? defaultValue;
|
|
}
|
|
|
|
public Single? GetFieldSingleNullable(DbDataReader dbDataReader, String columnName, Single? defaultValue)
|
|
{
|
|
int columnIdx = GetColumnOrdinal(dbDataReader, columnName);
|
|
if (columnIdx < 0) return defaultValue;
|
|
// Check for null value (to avoid throwing exception).
|
|
if (!dbDataReader.IsDBNull(columnIdx))
|
|
{
|
|
Type dataType = dbDataReader.GetFieldType(columnIdx);
|
|
// Expected data type.
|
|
if (dataType == typeof(Single))
|
|
return dbDataReader.GetFloat(columnIdx);
|
|
LogAlternativeTypeColumn(columnName, typeof(Single), dataType);
|
|
}
|
|
return defaultValue;
|
|
}
|
|
|
|
public Double GetFieldDouble(DbDataReader dbDataReader, String columnName, Double defaultValue)
|
|
{
|
|
return GetFieldDoubleNullable(dbDataReader, columnName, defaultValue) ?? defaultValue;
|
|
}
|
|
|
|
public Double? GetFieldDoubleNullable(DbDataReader dbDataReader, String columnName, Double? defaultValue)
|
|
{
|
|
int columnIdx = GetColumnOrdinal(dbDataReader, columnName);
|
|
if (columnIdx < 0) return defaultValue;
|
|
// Check for null value (to avoid throwing exception).
|
|
if (!dbDataReader.IsDBNull(columnIdx))
|
|
{
|
|
Type dataType = dbDataReader.GetFieldType(columnIdx);
|
|
// Expected data type.
|
|
if (dataType == typeof(Double))
|
|
return dbDataReader.GetDouble(columnIdx);
|
|
LogAlternativeTypeColumn(columnName, typeof(Double), dataType);
|
|
if (dataType == typeof(decimal))
|
|
return (double)dbDataReader.GetDecimal(columnIdx);
|
|
if (dataType == typeof(float))
|
|
return (double)dbDataReader.GetFloat(columnIdx);
|
|
}
|
|
return defaultValue;
|
|
}
|
|
|
|
public Decimal GetFieldDecimal(DbDataReader dbDataReader, String columnName, Decimal defaultValue)
|
|
{
|
|
return GetFieldDecimalNullable(dbDataReader, columnName, defaultValue) ?? defaultValue;
|
|
}
|
|
|
|
public Decimal? GetFieldDecimalNullable(DbDataReader dbDataReader, String columnName, Decimal? defaultValue = null)
|
|
{
|
|
int columnIdx = GetColumnOrdinal(dbDataReader, columnName);
|
|
if (columnIdx < 0) return defaultValue;
|
|
// Check for null value (to avoid throwing exception).
|
|
if (!dbDataReader.IsDBNull(columnIdx))
|
|
{
|
|
// Decimal data values may be alternative data types in result set.
|
|
// Avoid using try/catch as failures automatically log cast exceptions.
|
|
// Attempt to read value as decimal.
|
|
Type dataType = dbDataReader.GetFieldType(columnIdx);
|
|
// Expected data type.
|
|
if (dataType == typeof(Decimal))
|
|
return dbDataReader.GetDecimal(columnIdx);
|
|
LogAlternativeTypeColumn(columnName, typeof(Decimal), dataType);
|
|
// Allow 'double' value.
|
|
if (dataType == typeof(double))
|
|
return (decimal)dbDataReader.GetDouble(columnIdx);
|
|
// Allow 'float' value.
|
|
if (dataType == typeof(float))
|
|
return (decimal)dbDataReader.GetFloat(columnIdx);
|
|
}
|
|
return defaultValue;
|
|
}
|
|
|
|
public DateTime GetFieldDateTime(DbDataReader dbDataReader, String columnName, DateTime defaultValue)
|
|
{
|
|
return GetFieldDateTimeNullable(dbDataReader, columnName, defaultValue) ?? defaultValue;
|
|
}
|
|
|
|
public DateTime? GetFieldDateTimeNullable(DbDataReader dbDataReader, String columnName, DateTime? defaultValue)
|
|
{
|
|
int columnIdx = GetColumnOrdinal(dbDataReader, columnName);
|
|
if (columnIdx < 0) return defaultValue;
|
|
// Check for null value (to avoid throwing exception).
|
|
if (!dbDataReader.IsDBNull(columnIdx))
|
|
{
|
|
Type dataType = dbDataReader.GetFieldType(columnIdx);
|
|
// Expected data type.
|
|
if (dataType == typeof(DateTime))
|
|
return dbDataReader.GetDateTime(columnIdx);
|
|
LogAlternativeTypeColumn(columnName, typeof(DateTime), dataType);
|
|
}
|
|
return defaultValue;
|
|
}
|
|
|
|
public Guid GetFieldGuid(DbDataReader dbDataReader, String columnName, Guid defaultValue)
|
|
{
|
|
int columnIdx = GetColumnOrdinal(dbDataReader, columnName);
|
|
if (columnIdx < 0) return defaultValue;
|
|
// Check for null value (to avoid throwing exception).
|
|
if (!dbDataReader.IsDBNull(columnIdx))
|
|
{
|
|
Type dataType = dbDataReader.GetFieldType(columnIdx);
|
|
// Expected data type.
|
|
object data = dbDataReader[columnIdx];
|
|
if (data is Guid)
|
|
return (Guid)data;
|
|
LogAlternativeTypeColumn(columnName, typeof(Byte[]), dataType);
|
|
}
|
|
return defaultValue;
|
|
}
|
|
|
|
public byte[] GetFieldByteArray(DbDataReader dbDataReader, String columnName, byte[] defaultValue)
|
|
{
|
|
int columnIdx = GetColumnOrdinal(dbDataReader, columnName);
|
|
if (columnIdx < 0) return defaultValue;
|
|
// Check for null value (to avoid throwing exception).
|
|
if (!dbDataReader.IsDBNull(columnIdx))
|
|
{
|
|
Type dataType = dbDataReader.GetFieldType(columnIdx);
|
|
// Expected data type.
|
|
object data = dbDataReader[columnIdx];
|
|
if (data is byte[])
|
|
return (byte[])data;
|
|
LogAlternativeTypeColumn(columnName, typeof(Byte[]), dataType);
|
|
}
|
|
return defaultValue;
|
|
}
|
|
|
|
//-------------------------------------------------------------------
|
|
// Populate command parameters.
|
|
// Value is passed as a Typed parameter (rather than object) so that it's type is known
|
|
// even if the value is null, eg. to assign Db type for correct handling of null values,
|
|
// ref. byte[] -> VarBinary.
|
|
protected void AddCommandParameter<ValueType>(DbCommand dbCommand, String parameterName, ValueType value)
|
|
{
|
|
DbParameter dbParameter = dbCommand.CreateParameter();
|
|
dbParameter.ParameterName = parameterName;
|
|
dbParameter.Value = (Object)value ?? DBNull.Value;
|
|
if (typeof(ValueType) == typeof(Int16) || typeof(ValueType) == typeof(Int16?))
|
|
{
|
|
dbParameter.DbType = System.Data.DbType.Int16;
|
|
}
|
|
else if (typeof(ValueType) == typeof(Int32) || typeof(ValueType) == typeof(Int32?))
|
|
{
|
|
dbParameter.DbType = System.Data.DbType.Int32;
|
|
}
|
|
else if (typeof(ValueType) == typeof(DateTime) || typeof(ValueType) == typeof(DateTime?))
|
|
{
|
|
if (dbCommand is SqlCommand)
|
|
dbParameter.DbType = System.Data.DbType.DateTime2;
|
|
else dbParameter.DbType = System.Data.DbType.DateTime;
|
|
if ((Object)value == null || (DateTime)(Object)value == DateTime.MinValue || (DateTime)(Object)value == DateTime.MaxValue)
|
|
dbParameter.Value = DBNull.Value;
|
|
else
|
|
{
|
|
DateTime dateTime = (DateTime)dbParameter.Value;
|
|
// Resolve millisecond precision inconsistency between c# and odbc/sql.
|
|
//dbParameter.Value = new SqlDateTime((DateTime)dbParameter.Value);
|
|
dbParameter.Value = dateTime.AddMilliseconds(-dateTime.Millisecond);
|
|
// .Net 4.5
|
|
//dbParameter.Scale = 3;
|
|
}
|
|
}
|
|
else if (typeof(ValueType) == typeof(byte[]))
|
|
{
|
|
// Must be explicit with column type to handle null values correctly.
|
|
dbParameter.DbType = System.Data.DbType.Binary;
|
|
}
|
|
dbCommand.Parameters.Add(dbParameter);
|
|
}
|
|
|
|
//-------------------------------------------------------------------
|
|
// Database updates via ODBC Command.
|
|
public abstract bool ExecuteInsertCommand(DbCommand dbCommand);
|
|
public virtual bool ExecuteUpdateCommand(DbCommand dbCommand)
|
|
{
|
|
return ExecuteUpdateCommand(dbCommand, null);
|
|
}
|
|
public virtual bool ExecuteUpdateCommand(DbCommand dbCommand, List<String> columns = null)
|
|
{
|
|
throw new NotImplementedException($"ExecuteUpdateCommand(dbCmd,cols) not overridden. [{GetType().Name}]");
|
|
}
|
|
//public abstract bool ExecuteDeleteCommand (DbCommand dbCommand);
|
|
|
|
//-------------------------------------------------------------------
|
|
// Full list of all columns missing from database over life of application.
|
|
private static readonly List<String> columnsMissing = new List<string>();
|
|
// Missing columns are only logged on their initial discovery.
|
|
private void LogMissingColumn(String columnName)
|
|
{
|
|
String columnIdentifier = DbInfo.Context + "." + (!StringUtil.IsEmpty(SchemaName) ? SchemaName + "." : "") + TableName + "." + columnName;
|
|
if (!columnsMissing.ContainsCI(columnIdentifier))
|
|
{
|
|
columnsMissing.Add(columnIdentifier);
|
|
// Message is only logged on first occurence.
|
|
SaLogger.Debug(GetType().Name + " Column not found on database: " + columnIdentifier);
|
|
}
|
|
}
|
|
|
|
//-------------------------------------------------------------------
|
|
// Full list of all columns found with alternative data type in database over life of application.
|
|
private static readonly List<String> columnsAlternativeType = new List<string>();
|
|
// Missing columns are only logged on their initial discovery.
|
|
private void LogAlternativeTypeColumn(String columnName, Type typeExpected, Type typeFound)
|
|
{
|
|
String columnIdentifier = DbInfo.Context + "." + (!StringUtil.IsEmpty(SchemaName) ? SchemaName + "." : "") + TableName + "." + columnName;
|
|
if (!columnsAlternativeType.ContainsCI(columnIdentifier))
|
|
{
|
|
columnsAlternativeType.Add(columnIdentifier);
|
|
// Message is only logged on first occurence.
|
|
SaLogger.Debug(GetType().Name + " Column has inconsistent type: " + columnIdentifier + ". Expected: " + typeExpected.Name + ". Found: " + typeFound.Name);
|
|
}
|
|
}
|
|
|
|
//-------------------------------------------------------------------
|
|
// Logging.
|
|
public static SaMessageLevel LoggingLevel { get; set; } = SaMessageLevel.None;
|
|
public static String LoggingPrefix { get; set; } = " ~~~ ";
|
|
protected void Log(String fn, String message, SaMessageLevel level = SaMessageLevel.Debug)
|
|
{
|
|
if ((int)level > (int)LoggingLevel) return;
|
|
SaLogger.Log((fn + "\n\t" + message).Trim(), level);
|
|
}
|
|
|
|
//-------------------------------------------------------------------
|
|
}
|
|
//=======================================================================
|
|
}
|