using MySql.Data.MySqlClient; using Mysqlx.Resultset; using System; using System.Collections.Generic; using System.Linq; using System.Reflection; using System.Text; using System.Threading.Tasks; namespace MontoyaTech.MySqlPlus { /// /// A set of extensions to help work with MySqlColumns. /// public static class MySqlColumnExtensions { /// /// Gets the FieldInfo and MySqlColumn for an Id column on a given row and returns /// whether or not we found it. /// /// /// /// /// public static bool GetMySqlPrimaryKey(this T row, out FieldInfo field, out MySqlColumn idColumn) { var type = typeof(T); return GetMySqlPrimaryKey(type, out field, out idColumn); } /// /// Gets the FieldInfo and MySqlColumn for an Id column on a given row type and returns /// whether or not we found it. /// /// /// /// /// public static bool GetMySqlPrimaryKey(this Type type, out FieldInfo field, out MySqlColumn idColumn) { field = null; idColumn = null; var fields = type.GetFields(); if (fields == null || fields.Length == 0) return false; for (int i = 0; i < fields.Length; i++) { var column = fields[i].GetCustomAttribute(); if (column != null && column.PrimaryKey) { field = fields[i]; idColumn = column; return true; } } return false; } /// /// Gets the MySqlColumn type for a given column from a field and a column. /// /// /// /// public static string GetMySqlColumnType(this MySqlColumn column, FieldInfo field) { if (!string.IsNullOrWhiteSpace(column.Type)) return column.Type; if (column.Converter != null) return column.Converter.CreateInstance().ConvertToType; var typeCode = Type.GetTypeCode(field.FieldType); //If the field type is a nullable, get the actual field type. if (field.FieldType.IsNullable()) typeCode = Type.GetTypeCode(Nullable.GetUnderlyingType(field.FieldType)); switch (typeCode) { case TypeCode.Boolean: return "BOOLEAN"; case TypeCode.Byte: return "TINYINT UNSIGNED"; case TypeCode.Char: return "SMALLINT UNSIGNED"; case TypeCode.Decimal: return "DECIMAL"; case TypeCode.Double: return "DOUBLE"; case TypeCode.Single: return "FLOAT"; case TypeCode.Int16: return "SMALLINT"; case TypeCode.Int32: return "INT"; case TypeCode.Int64: return "BIGINT"; case TypeCode.String: return "LONGTEXT"; case TypeCode.UInt16: return "SMALLINT UNSIGNED"; case TypeCode.UInt32: return "INT UNSIGNED"; case TypeCode.UInt64: return "BIGINT UNSIGNED"; case TypeCode.SByte: return "TINYINT"; default: throw new NotSupportedException($"Unsupported column type code: {typeCode}"); } } /// /// Reads the value of a MySqlColumn from a MySqlDataReader and sets the value on the corresponding field on an instance of a row. /// /// /// /// /// /// /// public static void ReadValue(this MySqlColumn column, T row, FieldInfo field, MySqlDataReader reader) { //See if we can find the column index. int columnIndex = -1; int fields = reader.FieldCount; for (int i = 0; i < fields; i++) { string name = reader.GetName(i); if (name == column.Name || name == field.Name) { columnIndex = i; break; } } //If we didn't find the column, exit. if (columnIndex == -1) return; //See if we have a converter, use it to read the value. if (column.Converter != null) { field.SetValue(row, column.Converter.CreateInstance().ConvertFrom(reader.GetValue(columnIndex))); } else { //Get our field type code var typeCode = Type.GetTypeCode(field.FieldType); //If this field is a nullable, get the underlying type. bool nullable = false; if (field.FieldType.IsNullable()) { typeCode = Type.GetTypeCode(Nullable.GetUnderlyingType(field.FieldType)); nullable = true; } //Read the value based on the type code. switch (typeCode) { case TypeCode.Boolean: field.SetValue(row, reader.IsDBNull(columnIndex) ? (nullable ? null : default(bool)) : reader.GetValue(columnIndex).ConvertToType()); break; case TypeCode.Byte: field.SetValue(row, reader.IsDBNull(columnIndex) ? (nullable ? null : default(byte)) : reader.GetValue(columnIndex).ConvertToType()); break; case TypeCode.Char: field.SetValue(row, reader.IsDBNull(columnIndex) ? (nullable ? null : default(char)) : reader.GetValue(columnIndex).ConvertToType()); break; case TypeCode.Decimal: field.SetValue(row, reader.IsDBNull(columnIndex) ? (nullable ? null : default(decimal)) : reader.GetValue(columnIndex).ConvertToType()); break; case TypeCode.DateTime: field.SetValue(row, reader.IsDBNull(columnIndex) ? (nullable ? null : default(DateTime)) : reader.GetValue(columnIndex).ConvertToType()); break; case TypeCode.Double: field.SetValue(row, reader.IsDBNull(columnIndex) ? (nullable ? null : default(double)) : reader.GetValue(columnIndex).ConvertToType()); break; case TypeCode.Int16: field.SetValue(row, reader.IsDBNull(columnIndex) ? (nullable ? null : default(short)) : reader.GetValue(columnIndex).ConvertToType()); break; case TypeCode.Int32: field.SetValue(row, reader.IsDBNull(columnIndex) ? (nullable ? null : default(int)) : reader.GetValue(columnIndex).ConvertToType()); break; case TypeCode.Int64: field.SetValue(row, reader.IsDBNull(columnIndex) ? (nullable ? null : default(long)) : reader.GetValue(columnIndex).ConvertToType()); break; case TypeCode.SByte: field.SetValue(row, reader.IsDBNull(columnIndex) ? (nullable ? null : default(sbyte)) : reader.GetValue(columnIndex).ConvertToType()); break; case TypeCode.Single: field.SetValue(row, reader.IsDBNull(columnIndex) ? (nullable ? null : default(float)) : reader.GetValue(columnIndex).ConvertToType()); break; case TypeCode.String: field.SetValue(row, reader.IsDBNull(columnIndex) ? default(string) : reader.GetValue(columnIndex).ConvertToType()); break; case TypeCode.UInt16: field.SetValue(row, reader.IsDBNull(columnIndex) ? (nullable ? null : default(ushort)) : reader.GetValue(columnIndex).ConvertToType()); break; case TypeCode.UInt32: field.SetValue(row, reader.IsDBNull(columnIndex) ? (nullable ? null: default(uint)) : reader.GetValue(columnIndex).ConvertToType()); break; case TypeCode.UInt64: field.SetValue(row, reader.IsDBNull(columnIndex) ? (nullable ? null : default(ulong)) : reader.GetValue(columnIndex).ConvertToType()); break; default: throw new NotSupportedException("Unsupported TypeCode"); } } } /// /// Returns whether or not a MySqlColumn can be nullable or not based on the field definition and a row. /// /// /// /// /// /// public static bool IsNullable(this MySqlColumn column, FieldInfo field, T row) { //If the column is the primary key, it cannot be null. if (column.PrimaryKey) return false; var fieldType = field.FieldType; var defaultValue = field.GetValue(row); if (column.DefaultValue != null) defaultValue = column.DefaultValue; if (column.Converter != null) fieldType = column.Converter.CreateInstance().ConvertTo(defaultValue).GetType(); if (fieldType.IsNullable()) return true; if (fieldType.IsValueType) return false; return true; } } }