From 15e51b7a42b83cc5d601194318c596bbaaf2be82 Mon Sep 17 00:00:00 2001 From: MattMo Date: Wed, 22 Feb 2023 08:06:42 -0800 Subject: [PATCH] Simplified MySqlColumn by removing Default and Nullable. Added support for built in Nullable types. Bumped package version to 1.1.0 --- MySqlPlus.Example/Program.cs | 18 ++++-- MySqlPlus/MySqlColumn.cs | 10 ---- MySqlPlus/MySqlColumnExtensions.cs | 91 +++++++++++++++++++++-------- MySqlPlus/MySqlCommandExtensions.cs | 20 ++++--- MySqlPlus/MySqlPlus.csproj | 2 +- MySqlPlus/TypeExentions.cs | 10 ++++ 6 files changed, 102 insertions(+), 49 deletions(-) diff --git a/MySqlPlus.Example/Program.cs b/MySqlPlus.Example/Program.cs index 8de4c74..cd9bfcd 100644 --- a/MySqlPlus.Example/Program.cs +++ b/MySqlPlus.Example/Program.cs @@ -10,19 +10,22 @@ namespace MontoyaTech.MySqlPlus.Example [MySqlRowIndex("year", "year")] public class Car { - [MySqlColumn(Id = true, Name = "id", PrimaryKey = true, AutoIncrement = true, Nullable = false)] + [MySqlColumn(Id = true, Name = "id", PrimaryKey = true, AutoIncrement = true)] public ulong Id = 0; [MySqlColumn("make")] public string Make = null; - [MySqlColumn("model", Nullable = false, Type = "VARCHAR(255)")] + [MySqlColumn("model", Type = "VARCHAR(255)")] public string Model = "Unknown"; - [MySqlColumn("year", Nullable = false)] + [MySqlColumn("year")] public uint Year = 0; - [MySqlColumn("dateCreated", typeof(DateTimeToUnixConverter), DefaultValue = 0, Nullable = false)] + [MySqlColumn("compact")] + public bool? Compact = null; + + [MySqlColumn("dateCreated", typeof(DateTimeToUnixConverter), DefaultValue = 0)] public DateTime DateCreated = DateTime.UtcNow; } @@ -30,18 +33,21 @@ namespace MontoyaTech.MySqlPlus.Example { var session = new MySqlSession(""); + if (session.TableExists()) + session.DeleteTable(); + session.CreateTable(); session.DeleteAll(); - session.Insert(new Car() { Make = "Chevy", Model = "Camaro", Year = 2011 }); + session.Insert(new Car() { Make = "Chevy", Model = "Camaro", Year = 2011, Compact = true }); session.Insert(new Car() { Make = "GMC", Model = "Sierra", Year = 2000 }); var cars = session.GetAll(); foreach (var car in cars) - Console.WriteLine($"Make: {car.Make}, Model: {car.Model}, Year: {car.Year}, DateCreated: {car.DateCreated}"); + Console.WriteLine($"Make: {car.Make}, Model: {car.Model}, Year: {car.Year}, Compact: {car.Compact}, DateCreated: {car.DateCreated}"); cars[0].Make = "test"; diff --git a/MySqlPlus/MySqlColumn.cs b/MySqlPlus/MySqlColumn.cs index 2d4478d..f274e49 100644 --- a/MySqlPlus/MySqlColumn.cs +++ b/MySqlPlus/MySqlColumn.cs @@ -41,16 +41,6 @@ namespace MontoyaTech.MySqlPlus /// public bool AutoIncrement = false; - /// - /// Whether or not this column can be null, default is true. - /// - public bool Nullable = true; - - /// - /// Whether or not this column has a default value, default is true. - /// - public bool Default = true; - /// /// An overrided default value for this column if set, default is null. /// diff --git a/MySqlPlus/MySqlColumnExtensions.cs b/MySqlPlus/MySqlColumnExtensions.cs index bb3409f..de68981 100644 --- a/MySqlPlus/MySqlColumnExtensions.cs +++ b/MySqlPlus/MySqlColumnExtensions.cs @@ -20,13 +20,13 @@ namespace MontoyaTech.MySqlPlus /// /// /// - /// + /// /// - public static bool GetMySqlId(this T row, out FieldInfo idField, out MySqlColumn idColumn) + public static bool GetMySqlId(this T row, out FieldInfo field, out MySqlColumn idColumn) { var type = typeof(T); - return GetMySqlId(type, out idField, out idColumn); + return GetMySqlId(type, out field, out idColumn); } /// @@ -34,12 +34,12 @@ namespace MontoyaTech.MySqlPlus /// whether or not we found it. /// /// - /// + /// /// /// - public static bool GetMySqlId(this Type type, out FieldInfo idField, out MySqlColumn idColumn) + public static bool GetMySqlId(this Type type, out FieldInfo field, out MySqlColumn idColumn) { - idField = null; + field = null; idColumn = null; @@ -54,7 +54,7 @@ namespace MontoyaTech.MySqlPlus if (column != null && column.Id) { - idField = fields[i]; + field = fields[i]; idColumn = column; return true; @@ -78,9 +78,13 @@ namespace MontoyaTech.MySqlPlus if (column.Converter != null) return column.Converter.CreateInstance().ConvertToType; - var code = Type.GetTypeCode(field.FieldType); + var typeCode = Type.GetTypeCode(field.FieldType); - switch (code) + //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"; @@ -125,7 +129,7 @@ namespace MontoyaTech.MySqlPlus return "TINYINT"; default: - throw new NotSupportedException($"Unsupported column type code: {code}"); + throw new NotSupportedException($"Unsupported column type code: {typeCode}"); } } @@ -168,58 +172,97 @@ namespace MontoyaTech.MySqlPlus //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) ? default(bool) : reader.GetValue(columnIndex).ConvertToType()); + field.SetValue(row, reader.IsDBNull(columnIndex) ? (nullable ? null : default(bool)) : reader.GetValue(columnIndex).ConvertToType()); break; case TypeCode.Byte: - field.SetValue(row, reader.IsDBNull(columnIndex) ? default(byte) : reader.GetValue(columnIndex).ConvertToType()); + field.SetValue(row, reader.IsDBNull(columnIndex) ? (nullable ? null : default(byte)) : reader.GetValue(columnIndex).ConvertToType()); break; case TypeCode.Char: - field.SetValue(row, reader.IsDBNull(columnIndex) ? default(char) : reader.GetValue(columnIndex).ConvertToType()); + field.SetValue(row, reader.IsDBNull(columnIndex) ? (nullable ? null : default(char)) : reader.GetValue(columnIndex).ConvertToType()); break; case TypeCode.Decimal: - field.SetValue(row, reader.IsDBNull(columnIndex) ? default(decimal) : reader.GetValue(columnIndex).ConvertToType()); + field.SetValue(row, reader.IsDBNull(columnIndex) ? (nullable ? null : default(decimal)) : reader.GetValue(columnIndex).ConvertToType()); break; case TypeCode.DateTime: - field.SetValue(row, reader.IsDBNull(columnIndex) ? default(DateTime) : reader.GetValue(columnIndex).ConvertToType()); + field.SetValue(row, reader.IsDBNull(columnIndex) ? (nullable ? null : default(DateTime)) : reader.GetValue(columnIndex).ConvertToType()); break; case TypeCode.Double: - field.SetValue(row, reader.IsDBNull(columnIndex) ? default(double) : reader.GetValue(columnIndex).ConvertToType()); + field.SetValue(row, reader.IsDBNull(columnIndex) ? (nullable ? null : default(double)) : reader.GetValue(columnIndex).ConvertToType()); break; case TypeCode.Int16: - field.SetValue(row, reader.IsDBNull(columnIndex) ? default(short) : reader.GetValue(columnIndex).ConvertToType()); + field.SetValue(row, reader.IsDBNull(columnIndex) ? (nullable ? null : default(short)) : reader.GetValue(columnIndex).ConvertToType()); break; case TypeCode.Int32: - field.SetValue(row, reader.IsDBNull(columnIndex) ? default(int) : reader.GetValue(columnIndex).ConvertToType()); + field.SetValue(row, reader.IsDBNull(columnIndex) ? (nullable ? null : default(int)) : reader.GetValue(columnIndex).ConvertToType()); break; case TypeCode.Int64: - field.SetValue(row, reader.IsDBNull(columnIndex) ? default(long) : reader.GetValue(columnIndex).ConvertToType()); + field.SetValue(row, reader.IsDBNull(columnIndex) ? (nullable ? null : default(long)) : reader.GetValue(columnIndex).ConvertToType()); break; case TypeCode.SByte: - field.SetValue(row, reader.IsDBNull(columnIndex) ? default(sbyte) : reader.GetValue(columnIndex).ConvertToType()); + field.SetValue(row, reader.IsDBNull(columnIndex) ? (nullable ? null : default(sbyte)) : reader.GetValue(columnIndex).ConvertToType()); break; case TypeCode.Single: - field.SetValue(row, reader.IsDBNull(columnIndex) ? default(float) : reader.GetValue(columnIndex).ConvertToType()); + 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) ? default(ushort) : reader.GetValue(columnIndex).ConvertToType()); + field.SetValue(row, reader.IsDBNull(columnIndex) ? (nullable ? null : default(ushort)) : reader.GetValue(columnIndex).ConvertToType()); break; case TypeCode.UInt32: - field.SetValue(row, reader.IsDBNull(columnIndex) ? default(uint) : reader.GetValue(columnIndex).ConvertToType()); + field.SetValue(row, reader.IsDBNull(columnIndex) ? (nullable ? null: default(uint)) : reader.GetValue(columnIndex).ConvertToType()); break; case TypeCode.UInt64: - field.SetValue(row, reader.IsDBNull(columnIndex) ? default(ulong) : reader.GetValue(columnIndex).ConvertToType()); + 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) + { + 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; + } } } diff --git a/MySqlPlus/MySqlCommandExtensions.cs b/MySqlPlus/MySqlCommandExtensions.cs index 2a78766..4a85309 100644 --- a/MySqlPlus/MySqlCommandExtensions.cs +++ b/MySqlPlus/MySqlCommandExtensions.cs @@ -321,11 +321,14 @@ namespace MontoyaTech.MySqlPlus //Write the column data type builder.Append($"{column.GetMySqlColumnType(fields[i])} "); + //Determine if this column is nullable + bool nullable = column.IsNullable(field, row); + //Write the column null information - builder.Append($"{(column.Nullable ? "NULL" : "NOT NULL")} "); + builder.Append($"{(nullable ? "NULL" : "NOT NULL")} "); //Write the column default value if needed. (We can't do this if the column is auto increment) - if (column.Default && !column.AutoIncrement) + if (!column.AutoIncrement) { var defaultValue = column.DefaultValue; @@ -333,13 +336,14 @@ namespace MontoyaTech.MySqlPlus if (defaultValue == null) defaultValue = field.GetValue(row); - //Quirk, don't do this if the converter is null, and the default is null, and we are nullable. - if (!(column.Converter == null && defaultValue == null && column.Nullable)) - { - if (column.Converter != null) - defaultValue = column.Converter.CreateInstance().ConvertTo(defaultValue); + //If we have a converter, run the converter to get the true default value. + if (column.Converter != null) + defaultValue = column.Converter.CreateInstance().ConvertTo(defaultValue); - builder.Append($"{(column.Default ? $"DEFAULT @{i}" : "")} "); + //If we have a default value, set it on the column. + if (defaultValue != null) + { + builder.Append($"DEFAULT @{i} "); command.Parameters.AddWithValue($"@{i}", defaultValue); } diff --git a/MySqlPlus/MySqlPlus.csproj b/MySqlPlus/MySqlPlus.csproj index c31b3b7..3dd6716 100644 --- a/MySqlPlus/MySqlPlus.csproj +++ b/MySqlPlus/MySqlPlus.csproj @@ -7,7 +7,7 @@ MontoyaTech.MySqlPlus MontoyaTech.MySqlPlus MontoyaTech.MySqlPlus - 1.0.9 + 1.1.0 MontoyaTech A simple C# library to help work with MySql. MontoyaTech 2023 diff --git a/MySqlPlus/TypeExentions.cs b/MySqlPlus/TypeExentions.cs index af4bc2c..c8f76d6 100644 --- a/MySqlPlus/TypeExentions.cs +++ b/MySqlPlus/TypeExentions.cs @@ -635,5 +635,15 @@ namespace MontoyaTech.MySqlPlus } } } + + /// + /// Returns whether or not a given type is a System Nullabe type. + /// + /// + /// + public static bool IsNullable(this Type type) + { + return type.IsGenericType && type.GetGenericTypeDefinition().IsEquivalentTo(typeof(Nullable<>)); + } } }