diff --git a/MySqlPlus.Example/Program.cs b/MySqlPlus.Example/Program.cs index 9078171..4f59874 100644 --- a/MySqlPlus.Example/Program.cs +++ b/MySqlPlus.Example/Program.cs @@ -20,18 +20,51 @@ namespace MontoyaTech.MySqlPlus.Example [MySqlColumn("year")] public uint Year = 0; - //[MySqlColumn("dateCreated", typeof(DateTime2UnixConverter))] - //public DateTime DateCreated = DateTime.UtcNow; + [MySqlColumn("dateCreated", typeof(DateTime2UnixConverter))] + public DateTime DateCreated = DateTime.UtcNow; } - public class DateTime2UnixConverter + public class DateTime2UnixConverter : MySqlColumnConverter { + public object ConvertFrom(object input) + { + if (input is ulong unix) + { + if (unix == 0) + return DateTime.MinValue; + return new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc).AddSeconds(unix).ToUniversalTime(); + } + else + { + throw new NotSupportedException("Unsupported input object to convert from unix."); + } + } + + public object ConvertTo(object input) + { + if (input is DateTime dateTime) + { + if (dateTime == DateTime.MinValue) + return 0; + + if (dateTime == DateTime.MaxValue) + return ulong.MaxValue; + + return (ulong)(dateTime.ToUniversalTime().Subtract(new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc))).TotalSeconds; + } + else + { + throw new NotSupportedException("Unsupported input object to convert to unix."); + } + } } public static void Main(string[] args) { - var session = new MySqlSession("server=db.zone2d.com;user=root;database=zone2d;port=3306;password=-+W6!?Kv-6wDL2Vj5f=kC^Q&;SslMode=Required"); + var session = new MySqlSession(""); + + session.DeleteAll(); session.Insert(new Car() { Make = "Chevy", Model = "Camaro", Year = 2011 }); @@ -40,7 +73,7 @@ namespace MontoyaTech.MySqlPlus.Example var cars = session.GetAll(); foreach (var car in cars) - Console.WriteLine($"Make: {car.Make}, Model: {car.Model}, Year: {car.Year}"); + Console.WriteLine($"Make: {car.Make}, Model: {car.Model}, Year: {car.Year}, DateCreated: {car.DateCreated}"); cars[0].Make = "test"; diff --git a/MySqlPlus/MySqlColumn.cs b/MySqlPlus/MySqlColumn.cs index c0a48dd..e3b4a85 100644 --- a/MySqlPlus/MySqlColumn.cs +++ b/MySqlPlus/MySqlColumn.cs @@ -16,21 +16,52 @@ namespace MontoyaTech.MySqlPlus [AttributeUsage(AttributeTargets.Field, AllowMultiple = false, Inherited = true)] public class MySqlColumn : Attribute { + /// + /// Whether or not this column is the row id. + /// public bool Id = false; + /// + /// The name of the column, if null, the field name will be used instead. + /// public string Name = null; + /// + /// An optional MySqlColumnConverter that can convert this column to another + /// data type when needed. + /// public Type Converter = null; + /// + /// Creates a new default MySqlColumn. + /// public MySqlColumn() { } + /// + /// Creates a new MySqlColumn with a name and column converter if needed. + /// + /// + /// + /// public MySqlColumn(string name, Type converter = null) { this.Name = name; + //Make sure the converter is valid if one was passed. + if (converter != null && !converter.IsAssignableTo(typeof(MySqlColumnConverter))) + throw new NotSupportedException($"Converter must inherit {nameof(MySqlColumnConverter)}"); + this.Converter = converter; } + /// + /// Reads this column from a given data reader and stores the value into the provided field on a row. + /// + /// + /// + /// + /// + /// public void ReadValue(T row, FieldInfo field, MySqlDataReader reader) { //See if we can find the column index. @@ -51,59 +82,67 @@ namespace MontoyaTech.MySqlPlus if (columnIndex == -1) return; - //Get our field type code - var typeCode = Type.GetTypeCode(field.FieldType); - - //Read the value based on the type code. - switch (typeCode) + //See if we have a converter, use it to read the value. + if (this.Converter != null) { - case TypeCode.Boolean: - field.SetValue(row, reader.IsDBNull(columnIndex) ? default(bool) : reader.GetValue(columnIndex).ConvertToType()); - break; - case TypeCode.Byte: - field.SetValue(row, reader.IsDBNull(columnIndex) ? default(byte) : reader.GetValue(columnIndex).ConvertToType()); - break; - case TypeCode.Char: - field.SetValue(row, reader.IsDBNull(columnIndex) ? default(char) : reader.GetValue(columnIndex).ConvertToType()); - break; - case TypeCode.Decimal: - field.SetValue(row, reader.IsDBNull(columnIndex) ? default(decimal) : reader.GetValue(columnIndex).ConvertToType()); - break; - case TypeCode.DateTime: - field.SetValue(row, reader.IsDBNull(columnIndex) ? default(DateTime) : reader.GetValue(columnIndex).ConvertToType()); - break; - case TypeCode.Double: - field.SetValue(row, reader.IsDBNull(columnIndex) ? default(double) : reader.GetValue(columnIndex).ConvertToType()); - break; - case TypeCode.Int16: - field.SetValue(row, reader.IsDBNull(columnIndex) ? default(short) : reader.GetValue(columnIndex).ConvertToType()); - break; - case TypeCode.Int32: - field.SetValue(row, reader.IsDBNull(columnIndex) ? default(int) : reader.GetValue(columnIndex).ConvertToType()); - break; - case TypeCode.Int64: - field.SetValue(row, reader.IsDBNull(columnIndex) ? default(long) : reader.GetValue(columnIndex).ConvertToType()); - break; - case TypeCode.SByte: - field.SetValue(row, reader.IsDBNull(columnIndex) ? default(sbyte) : reader.GetValue(columnIndex).ConvertToType()); - break; - case TypeCode.Single: - field.SetValue(row, reader.IsDBNull(columnIndex) ? 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()); - break; - case TypeCode.UInt32: - field.SetValue(row, reader.IsDBNull(columnIndex) ? default(uint) : reader.GetValue(columnIndex).ConvertToType()); - break; - case TypeCode.UInt64: - field.SetValue(row, reader.IsDBNull(columnIndex) ? default(ulong) : reader.GetValue(columnIndex).ConvertToType()); - break; - default: - throw new NotSupportedException("Unsupported TypeCode"); + field.SetValue(row, this.Converter.CreateInstance().ConvertFrom(reader.GetValue(columnIndex))); + } + else + { + //Get our field type code + var typeCode = Type.GetTypeCode(field.FieldType); + + //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()); + break; + case TypeCode.Byte: + field.SetValue(row, reader.IsDBNull(columnIndex) ? default(byte) : reader.GetValue(columnIndex).ConvertToType()); + break; + case TypeCode.Char: + field.SetValue(row, reader.IsDBNull(columnIndex) ? default(char) : reader.GetValue(columnIndex).ConvertToType()); + break; + case TypeCode.Decimal: + field.SetValue(row, reader.IsDBNull(columnIndex) ? default(decimal) : reader.GetValue(columnIndex).ConvertToType()); + break; + case TypeCode.DateTime: + field.SetValue(row, reader.IsDBNull(columnIndex) ? default(DateTime) : reader.GetValue(columnIndex).ConvertToType()); + break; + case TypeCode.Double: + field.SetValue(row, reader.IsDBNull(columnIndex) ? default(double) : reader.GetValue(columnIndex).ConvertToType()); + break; + case TypeCode.Int16: + field.SetValue(row, reader.IsDBNull(columnIndex) ? default(short) : reader.GetValue(columnIndex).ConvertToType()); + break; + case TypeCode.Int32: + field.SetValue(row, reader.IsDBNull(columnIndex) ? default(int) : reader.GetValue(columnIndex).ConvertToType()); + break; + case TypeCode.Int64: + field.SetValue(row, reader.IsDBNull(columnIndex) ? default(long) : reader.GetValue(columnIndex).ConvertToType()); + break; + case TypeCode.SByte: + field.SetValue(row, reader.IsDBNull(columnIndex) ? default(sbyte) : reader.GetValue(columnIndex).ConvertToType()); + break; + case TypeCode.Single: + field.SetValue(row, reader.IsDBNull(columnIndex) ? 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()); + break; + case TypeCode.UInt32: + field.SetValue(row, reader.IsDBNull(columnIndex) ? default(uint) : reader.GetValue(columnIndex).ConvertToType()); + break; + case TypeCode.UInt64: + field.SetValue(row, reader.IsDBNull(columnIndex) ? default(ulong) : reader.GetValue(columnIndex).ConvertToType()); + break; + default: + throw new NotSupportedException("Unsupported TypeCode"); + } } } } diff --git a/MySqlPlus/MySqlColumnConverter.cs b/MySqlPlus/MySqlColumnConverter.cs new file mode 100644 index 0000000..fb48f46 --- /dev/null +++ b/MySqlPlus/MySqlColumnConverter.cs @@ -0,0 +1,29 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace MontoyaTech.MySqlPlus +{ + /// + /// The outline of a MySqlColumnConverter that can convert a + /// columns value from one type to another and back. + /// + public interface MySqlColumnConverter + { + /// + /// Converts the input value to the desired type the database expects. + /// + /// + /// + object ConvertTo(object input); + + /// + /// Converts the input value from a database type to the desired type. + /// + /// + /// + object ConvertFrom(object input); + } +} diff --git a/MySqlPlus/MySqlCommandExtensions.cs b/MySqlPlus/MySqlCommandExtensions.cs index 37005b5..9bd8c2d 100644 --- a/MySqlPlus/MySqlCommandExtensions.cs +++ b/MySqlPlus/MySqlCommandExtensions.cs @@ -60,7 +60,7 @@ namespace MontoyaTech.MySqlPlus builder.Append($"`{(string.IsNullOrWhiteSpace(column.Name) ? fields[i].Name : column.Name)}` = @{i}"); - command.Parameters.AddWithValue($"@{i}", fields[i].GetValue(row)); + command.Parameters.AddWithValue($"@{i}", (column.Converter == null ? fields[i].GetValue(row) : column.Converter.CreateInstance().ConvertTo(fields[i].GetValue(row)))); seperate = true; } @@ -122,7 +122,7 @@ namespace MontoyaTech.MySqlPlus builder.Append($"`{(string.IsNullOrWhiteSpace(column.Name) ? fields[i].Name : column.Name)}` = @{i}"); - command.Parameters.AddWithValue($"@{i}", fields[i].GetValue(row)); + command.Parameters.AddWithValue($"@{i}", (column.Converter == null ? fields[i].GetValue(row) : column.Converter.CreateInstance().ConvertTo(fields[i].GetValue(row)))); seperate = true; } @@ -257,5 +257,28 @@ namespace MontoyaTech.MySqlPlus //Set the command text. command.CommandText = builder.ToString(); } + + /// + /// Setups this MySqlCommand to delete all rows of a given type from the db. + /// + /// + /// + public static void DeleteAll(this MySqlCommand command) + { + //Get the type of T + var type = typeof(T); + + //Get the row information. + var rowAttribute = type.GetCustomAttribute(); + + //Start building the query. + var builder = new StringBuilder(); + + //Write the delete from section + builder.Append($"DELETE FROM `{(rowAttribute == null || string.IsNullOrWhiteSpace(rowAttribute.Name) ? type.Name : rowAttribute.Name)}`"); + + //Set the command text. + command.CommandText = builder.ToString(); + } } } diff --git a/MySqlPlus/MySqlPlus.csproj b/MySqlPlus/MySqlPlus.csproj index 33699d0..5e557ac 100644 --- a/MySqlPlus/MySqlPlus.csproj +++ b/MySqlPlus/MySqlPlus.csproj @@ -1,4 +1,4 @@ - + net6.0 diff --git a/MySqlPlus/MySqlSession.cs b/MySqlPlus/MySqlSession.cs index 45f67e9..2072686 100644 --- a/MySqlPlus/MySqlSession.cs +++ b/MySqlPlus/MySqlSession.cs @@ -174,6 +174,21 @@ namespace MontoyaTech.MySqlPlus } } + /// + /// Deletes all the rows of a given type in the db. + /// + /// The type of row to delete. + /// The number of rows affected + public int DeleteAll() + { + using (var command = new MySqlCommand()) + { + command.DeleteAll(); + + return this.Connection.ExecuteNonQuery(command); + } + } + /// /// Implicitly converts a MySqlSession to a MySqlConnection. ///