From 91eb7fea58746b8cd8b2046db163dd333bb15dba Mon Sep 17 00:00:00 2001 From: Serraniel Date: Tue, 15 Nov 2022 22:12:50 +0100 Subject: [PATCH] Added storage projects --- Docs/SweetLib.Storage | 112 ++++++++++++++++ Docs/SweetLib.Storage.MySql | 8 ++ Docs/SweetLib.Storage.md | 7 + SweetLib.Storage.MySql/CustomMySqlObject.cs | 121 ++++++++++++++++++ .../Helper/DbParameterExtensions.cs | 17 +++ .../SweetLib.Storage.MySql.csproj | 47 +++++++ .../SweetLib.Storage.MySql.nuspec | 18 +++ SweetLib.Storage.MySql/build-nuget.bat | 2 + .../Database/IConnectionProvider.cs | 17 +++ SweetLib.Storage/Database/IDatabaseObject.cs | 39 ++++++ SweetLib.Storage/IIdGenerator.cs | 23 ++++ SweetLib.Storage/IIdentified.cs | 20 +++ SweetLib.Storage/IStorable.cs | 30 +++++ SweetLib.Storage/SweetLib.Storage.csproj | 1 - SweetLib.sln | 6 + 15 files changed, 467 insertions(+), 1 deletion(-) create mode 100644 Docs/SweetLib.Storage create mode 100644 Docs/SweetLib.Storage.MySql create mode 100644 Docs/SweetLib.Storage.md create mode 100644 SweetLib.Storage.MySql/CustomMySqlObject.cs create mode 100644 SweetLib.Storage.MySql/Helper/DbParameterExtensions.cs create mode 100644 SweetLib.Storage.MySql/SweetLib.Storage.MySql.csproj create mode 100644 SweetLib.Storage.MySql/SweetLib.Storage.MySql.nuspec create mode 100644 SweetLib.Storage.MySql/build-nuget.bat create mode 100644 SweetLib.Storage/Database/IConnectionProvider.cs create mode 100644 SweetLib.Storage/Database/IDatabaseObject.cs create mode 100644 SweetLib.Storage/IIdGenerator.cs create mode 100644 SweetLib.Storage/IIdentified.cs create mode 100644 SweetLib.Storage/IStorable.cs diff --git a/Docs/SweetLib.Storage b/Docs/SweetLib.Storage new file mode 100644 index 0000000..9256e24 --- /dev/null +++ b/Docs/SweetLib.Storage @@ -0,0 +1,112 @@ + + + + SweetLib.Storage + + + + + Provider for database connection. + + Specific type of the database connection. Must inherit . + + + + Gets an instance of a database connection. + + + + + + Object to store in database. + + Type of the used identifier. + + + + Assigns field names of the database columns. + + + + + + Assigns values to the query parameters. + + + + + + + Assigns values to the object. + + + + + + + Table name of the object. + + + + + + Interface for identified objects. + + Type of the identifier. + + + + Identifier of the object. + + + + + Gets a dummy Id for unidentified instances; + + + + + + Generates a new object id. + + Type of the used identifier. + + + + Generates a new id of the specified type. + + + + + + Generates a new id of the specified type. Runs asynchronous. + + + + + + Interface for objects which can be stored and restored. + + Type of the used identifier. + + + + Stores the object. + + + + + + Restores an object. + + Identifier of the object to restore. + + + + + Determines whether the current object instance is new or already exists. + + Boolean indicating if the current object instance is new or already exists. + + + diff --git a/Docs/SweetLib.Storage.MySql b/Docs/SweetLib.Storage.MySql new file mode 100644 index 0000000..1a3079d --- /dev/null +++ b/Docs/SweetLib.Storage.MySql @@ -0,0 +1,8 @@ + + + + SweetLib.Storage.MySql + + + + diff --git a/Docs/SweetLib.Storage.md b/Docs/SweetLib.Storage.md new file mode 100644 index 0000000..5401267 --- /dev/null +++ b/Docs/SweetLib.Storage.md @@ -0,0 +1,7 @@ + +# Contents [#](#contents 'Go To Here') + + + + +# SweetLib.Storage.MySql [#](#assembly 'Go To Here') [=](#contents 'Back To Contents') diff --git a/SweetLib.Storage.MySql/CustomMySqlObject.cs b/SweetLib.Storage.MySql/CustomMySqlObject.cs new file mode 100644 index 0000000..2db43a2 --- /dev/null +++ b/SweetLib.Storage.MySql/CustomMySqlObject.cs @@ -0,0 +1,121 @@ +using System.Collections.Generic; +using System.Data.Common; +using System.Linq; +using System.Threading.Tasks; +using MySql.Data.MySqlClient; +using SweetLib.Storage.Database; +using SweetLib.Storage.MySql.Helper; + +namespace SweetLib.Storage.MySql; + +public abstract class CustomMySqlObject : IDatabaseObject +{ + protected CustomMySqlObject(IConnectionProvider connectionProvider, + IIdGenerator idGenerator) + { + ConnectionProvider = connectionProvider; + IdGenerator = idGenerator; + Id = UnidentifiedId(); + } + + protected IConnectionProvider ConnectionProvider { get; } + protected IIdGenerator IdGenerator { get; } + + public T Id { get; set; } + public abstract T UnidentifiedId(); + + public async Task Store() + { + var wasNew = IsNew(); + + var connection = ConnectionProvider!.GetConnection(); + await connection.OpenAsync(); + try + { + var command = connection.CreateCommand(); + try + { + command.CommandText = wasNew ? InsertCommand() : UpdateCommand(); + await AssignToAsync(command.Parameters); + await command.ExecuteNonQueryAsync(); + } + catch + { + // Avoid invalid ID if new object could not be stored + if (wasNew) + Id = UnidentifiedId(); + + throw; + } + } + finally + { + await connection.CloseAsync(); + } + } + + public bool IsNew() + { + return Id.Equals(UnidentifiedId()); + } + + public IEnumerable AssignFieldNames() + { + var result = new List(); + result.Add(IdFieldName()); + + return result; + } + + public async Task AssignToAsync(DbParameterCollection parameters) + { + if (IsNew()) Id = await IdGenerator.GenerateNewIdAsync(); + + parameters.AddMySqlParameterWithValue("@Id", Id); + } + + public async Task AssignFromAsync(DbDataReader reader) + { + Id = (T)reader["Id"]; + } + + public abstract string TableName(); + + public async Task Restore(T identifier) + { + var connection = ConnectionProvider!.GetConnection(); + await connection.OpenAsync(); + try + { + var command = connection.CreateCommand(); + command.CommandText = $"SELECT * FROM {TableName()} WHERE Id=@id"; + command.Parameters.AddWithValue("@id", identifier); + + var reader = await command.ExecuteReaderAsync(); + if (!reader.HasRows) + return false; + + await AssignFromAsync(reader); + return true; + } + finally + { + await connection.CloseAsync(); + } + } + + internal string InsertCommand() + { + var fields = AssignFieldNames(); + return + $"INSERT INTO {TableName()} ({string.Join(",", fields)}) VALUES ({string.Join(",", fields.Select(f => $"@{f}"))})"; + } + + internal string UpdateCommand() + { + var fields = AssignFieldNames(); + return $"UPDATE {TableName()} SET {string.Join(",", fields.Select(f => $"{f}=@{f}"))}"; + } + + public abstract string IdFieldName(); +} \ No newline at end of file diff --git a/SweetLib.Storage.MySql/Helper/DbParameterExtensions.cs b/SweetLib.Storage.MySql/Helper/DbParameterExtensions.cs new file mode 100644 index 0000000..6bbc2f0 --- /dev/null +++ b/SweetLib.Storage.MySql/Helper/DbParameterExtensions.cs @@ -0,0 +1,17 @@ +using System.Data.Common; +using MySql.Data.MySqlClient; + +namespace SweetLib.Storage.MySql.Helper +{ + public static class MySqlDbParameterExtensions + { + public static MySqlParameter AddMySqlParameterWithValue(this DbParameterCollection parameterCollection, + string name, object value) + { + var param = new MySqlParameter(name, value); + parameterCollection.Add(param); + + return param; + } + } +} \ No newline at end of file diff --git a/SweetLib.Storage.MySql/SweetLib.Storage.MySql.csproj b/SweetLib.Storage.MySql/SweetLib.Storage.MySql.csproj new file mode 100644 index 0000000..74d7e51 --- /dev/null +++ b/SweetLib.Storage.MySql/SweetLib.Storage.MySql.csproj @@ -0,0 +1,47 @@ + + + + netstandard2.0 + 0.1.0-alpha.1 + SweetLib MySql Storage package + Serraniel + + Copyright © 2017-2022 by Serraniel + + https://github.com/Serraniel/SweetLib + https://github.com/Serraniel/SweetLib + true + https://github.com/Serraniel/SweetLib/blob/develop/nuget_icon.png?raw=true + true + LICENSE + 0.1.0.3 + 0.1.0.3 + latest + + + + bin\Release\netstandard1.3\SweetLib.IO.xml + + + + ..\Docs\SweetLib.Storage.MySql + + + + + + + + + + + True + + + + + + + + + diff --git a/SweetLib.Storage.MySql/SweetLib.Storage.MySql.nuspec b/SweetLib.Storage.MySql/SweetLib.Storage.MySql.nuspec new file mode 100644 index 0000000..60041e9 --- /dev/null +++ b/SweetLib.Storage.MySql/SweetLib.Storage.MySql.nuspec @@ -0,0 +1,18 @@ + + + + $id$ + $version$-alpha + $title$ + Serraniel + $author$ + https://opensource.org/licenses/GPL-3.0 + https://github.com/Serraniel/SweetLib + https://github.com/Serraniel/SweetLib/blob/develop/nuget_icon.png?raw=true + true + $description$ + First pre version just to try that nuget package thing :) + Copyright © 2017 Serraniel + Utility + + \ No newline at end of file diff --git a/SweetLib.Storage.MySql/build-nuget.bat b/SweetLib.Storage.MySql/build-nuget.bat new file mode 100644 index 0000000..f341b7c --- /dev/null +++ b/SweetLib.Storage.MySql/build-nuget.bat @@ -0,0 +1,2 @@ +nuget pack SweetLib.Storage.MySql.csproj -properties Configuration=Release -symbols +pause \ No newline at end of file diff --git a/SweetLib.Storage/Database/IConnectionProvider.cs b/SweetLib.Storage/Database/IConnectionProvider.cs new file mode 100644 index 0000000..c5caa30 --- /dev/null +++ b/SweetLib.Storage/Database/IConnectionProvider.cs @@ -0,0 +1,17 @@ +using System.Data.Common; + +namespace SweetLib.Storage.Database +{ + /// + /// Provider for database connection. + /// + /// Specific type of the database connection. Must inherit . + public interface IConnectionProvider where T : DbConnection + { + /// + /// Gets an instance of a database connection. + /// + /// + T GetConnection(); + } +} \ No newline at end of file diff --git a/SweetLib.Storage/Database/IDatabaseObject.cs b/SweetLib.Storage/Database/IDatabaseObject.cs new file mode 100644 index 0000000..b579fa8 --- /dev/null +++ b/SweetLib.Storage/Database/IDatabaseObject.cs @@ -0,0 +1,39 @@ +using System.Collections.Generic; +using System.Data.Common; +using System.Threading.Tasks; + +namespace SweetLib.Storage.Database +{ + /// + /// Object to store in database. + /// + /// Type of the used identifier. + public interface IDatabaseObject : IStorable + { + /// + /// Assigns field names of the database columns. + /// + /// + IEnumerable AssignFieldNames(); + + /// + /// Assigns values to the query parameters. + /// + /// + /// + Task AssignToAsync(DbParameterCollection parameters); + + /// + /// Assigns values to the object. + /// + /// + /// + Task AssignFromAsync(DbDataReader reader); + + /// + /// Table name of the object. + /// + /// + string TableName(); + } +} \ No newline at end of file diff --git a/SweetLib.Storage/IIdGenerator.cs b/SweetLib.Storage/IIdGenerator.cs new file mode 100644 index 0000000..ab5a885 --- /dev/null +++ b/SweetLib.Storage/IIdGenerator.cs @@ -0,0 +1,23 @@ +using System.Threading.Tasks; + +namespace SweetLib.Storage +{ + /// + /// Generates a new object id. + /// + /// Type of the used identifier. + public interface IIdGenerator + { + /// + /// Generates a new id of the specified type. + /// + /// + T GenerateNewId(); + + /// + /// Generates a new id of the specified type. Runs asynchronous. + /// + /// + Task GenerateNewIdAsync(); + } +} \ No newline at end of file diff --git a/SweetLib.Storage/IIdentified.cs b/SweetLib.Storage/IIdentified.cs new file mode 100644 index 0000000..5185b44 --- /dev/null +++ b/SweetLib.Storage/IIdentified.cs @@ -0,0 +1,20 @@ +namespace SweetLib.Storage +{ + /// + /// Interface for identified objects. + /// + /// Type of the identifier. + public interface IIdentified + { + /// + /// Identifier of the object. + /// + T Id { get; set; } + + /// + /// Gets a dummy Id for unidentified instances; + /// + /// + T UnidentifiedId(); + } +} \ No newline at end of file diff --git a/SweetLib.Storage/IStorable.cs b/SweetLib.Storage/IStorable.cs new file mode 100644 index 0000000..101ff3e --- /dev/null +++ b/SweetLib.Storage/IStorable.cs @@ -0,0 +1,30 @@ +using System.Threading.Tasks; + +namespace SweetLib.Storage +{ + /// + /// Interface for objects which can be stored and restored. + /// + /// Type of the used identifier. + public interface IStorable : IIdentified + { + /// + /// Stores the object. + /// + /// + Task Store(); + + /// + /// Restores an object. + /// + /// Identifier of the object to restore. + /// + Task Restore(T identifier); + + /// + /// Determines whether the current object instance is new or already exists. + /// + /// Boolean indicating if the current object instance is new or already exists. + bool IsNew(); + } +} \ No newline at end of file diff --git a/SweetLib.Storage/SweetLib.Storage.csproj b/SweetLib.Storage/SweetLib.Storage.csproj index cdd0238..a60a9ce 100644 --- a/SweetLib.Storage/SweetLib.Storage.csproj +++ b/SweetLib.Storage/SweetLib.Storage.csproj @@ -28,7 +28,6 @@ - diff --git a/SweetLib.sln b/SweetLib.sln index c774c87..accdee1 100644 --- a/SweetLib.sln +++ b/SweetLib.sln @@ -17,6 +17,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SweetLib.IO", "SweetLib.IO\ EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SweetLib.Storage", "SweetLib.Storage\SweetLib.Storage.csproj", "{E08BDA4B-BA1E-44F8-9D97-D11F80F8F9F0}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SweetLib.Storage.MySql", "SweetLib.Storage.MySql\SweetLib.Storage.MySql.csproj", "{EEDC1D23-1CA9-4A1D-85CB-C71E73E10B12}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -43,6 +45,10 @@ Global {E08BDA4B-BA1E-44F8-9D97-D11F80F8F9F0}.Debug|Any CPU.Build.0 = Debug|Any CPU {E08BDA4B-BA1E-44F8-9D97-D11F80F8F9F0}.Release|Any CPU.ActiveCfg = Release|Any CPU {E08BDA4B-BA1E-44F8-9D97-D11F80F8F9F0}.Release|Any CPU.Build.0 = Release|Any CPU + {EEDC1D23-1CA9-4A1D-85CB-C71E73E10B12}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {EEDC1D23-1CA9-4A1D-85CB-C71E73E10B12}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EEDC1D23-1CA9-4A1D-85CB-C71E73E10B12}.Release|Any CPU.ActiveCfg = Release|Any CPU + {EEDC1D23-1CA9-4A1D-85CB-C71E73E10B12}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE