Compare commits

..

4 commits

16 changed files with 479 additions and 2 deletions

111
Docs/SweetLib.Storage Normal file
View file

@ -0,0 +1,111 @@
<?xml version="1.0"?>
<doc>
<assembly>
<name>SweetLib.Storage</name>
</assembly>
<members>
<member name="T:SweetLib.Storage.Database.IDatabaseObject`1">
<summary>
Object to store in database.
</summary>
<typeparam name="T">Type of the used identifier.</typeparam>
</member>
<member name="M:SweetLib.Storage.Database.IDatabaseObject`1.AssignFieldNames">
<summary>
Assigns field names of the database columns.
</summary>
<returns></returns>
</member>
<member name="M:SweetLib.Storage.Database.IDatabaseObject`1.AssignToAsync(System.Data.Common.DbParameterCollection)">
<summary>
Assigns values to the query parameters.
</summary>
<param name="parameters"></param>
<returns></returns>
</member>
<member name="M:SweetLib.Storage.Database.IDatabaseObject`1.AssignFromAsync(System.Data.Common.DbDataReader)">
<summary>
Assigns values to the object.
</summary>
<param name="reader"></param>
<returns></returns>
</member>
<member name="M:SweetLib.Storage.Database.IDatabaseObject`1.TableName">
<summary>
Table name of the object.
</summary>
<returns></returns>
</member>
<member name="T:SweetLib.Storage.IConnectionProvider`1">
<summary>
Provider for database connection.
</summary>
</member>
<member name="M:SweetLib.Storage.IConnectionProvider`1.GetConnection">
<summary>
Gets an instance of a database connection.
</summary>
<returns></returns>
</member>
<member name="T:SweetLib.Storage.IIdentified`1">
<summary>
Interface for identified objects.
</summary>
<typeparam name="T">Type of the identifier.</typeparam>
</member>
<member name="P:SweetLib.Storage.IIdentified`1.Id">
<summary>
Identifier of the object.
</summary>
</member>
<member name="M:SweetLib.Storage.IIdentified`1.UnidentifiedId">
<summary>
Gets a dummy Id for unidentified instances;
</summary>
<returns></returns>
</member>
<member name="T:SweetLib.Storage.IIdGenerator`1">
<summary>
Generates a new object id.
</summary>
<typeparam name="T">Type of the used identifier.</typeparam>
</member>
<member name="M:SweetLib.Storage.IIdGenerator`1.GenerateNewId">
<summary>
Generates a new id of the specified type.
</summary>
<returns></returns>
</member>
<member name="M:SweetLib.Storage.IIdGenerator`1.GenerateNewIdAsync">
<summary>
Generates a new id of the specified type. Runs asynchronous.
</summary>
<returns></returns>
</member>
<member name="T:SweetLib.Storage.IStorable`1">
<summary>
Interface for objects which can be stored and restored.
</summary>
<typeparam name="T">Type of the used identifier.</typeparam>
</member>
<member name="M:SweetLib.Storage.IStorable`1.Store">
<summary>
Stores the object.
</summary>
<returns></returns>
</member>
<member name="M:SweetLib.Storage.IStorable`1.Restore(`0)">
<summary>
Restores an object.
</summary>
<param name="identifier">Identifier of the object to restore.</param>
<returns></returns>
</member>
<member name="M:SweetLib.Storage.IStorable`1.IsNew">
<summary>
Determines whether the current object instance is new or already exists.
</summary>
<returns>Boolean indicating if the current object instance is new or already exists.</returns>
</member>
</members>
</doc>

View file

@ -0,0 +1,8 @@
<?xml version="1.0"?>
<doc>
<assembly>
<name>SweetLib.Storage.MySql</name>
</assembly>
<members>
</members>
</doc>

7
Docs/SweetLib.Storage.md Normal file
View file

@ -0,0 +1,7 @@
<a name='contents'></a>
# Contents [#](#contents 'Go To Here')
<a name='assembly'></a>
# SweetLib.Storage.MySql [#](#assembly 'Go To Here') [=](#contents 'Back To Contents')

View file

@ -0,0 +1,121 @@
using System.Collections.Generic;
using System.Data.Common;
using System.Linq;
using System.Threading.Tasks;
using SweetLib.Storage.Database;
using SweetLib.Storage.MySql.Helper;
namespace SweetLib.Storage.MySql;
public abstract class CustomMySqlObject<T> : IDatabaseObject<T>
{
protected CustomMySqlObject(IDatabaseConnectionProvider connectionProvider,
IIdGenerator<T> idGenerator)
{
ConnectionProvider = connectionProvider;
IdGenerator = idGenerator;
Id = UnidentifiedId();
}
protected IDatabaseConnectionProvider ConnectionProvider { get; }
protected IIdGenerator<T> 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
{
connection.Close();
}
}
public bool IsNew()
{
return Id.Equals(UnidentifiedId());
}
public virtual IEnumerable<string> AssignFieldNames()
{
var result = new List<string>();
result.Add(IdFieldName());
return result;
}
public virtual async Task AssignToAsync(DbParameterCollection parameters)
{
if (IsNew()) Id = await IdGenerator.GenerateNewIdAsync();
parameters.AddMySqlParameterWithValue("@Id", Id);
}
public virtual async Task AssignFromAsync(DbDataReader reader)
{
await reader.ReadAsync();
Id = (T)reader["Id"];
}
public abstract string TableName();
public async Task<bool> 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.AddMySqlParameterWithValue("@id", identifier);
var reader = await command.ExecuteReaderAsync();
if (!reader.HasRows)
return false;
await AssignFromAsync(reader);
return true;
}
finally
{
connection.Close();
}
}
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();
}

View file

@ -0,0 +1,16 @@
using System.Data.Common;
using MySqlConnector;
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;
}
}

View file

@ -0,0 +1,50 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<Version>0.1.0-alpha.1</Version>
<Description>SweetLib MySql Storage package</Description>
<Authors>Serraniel</Authors>
<Company />
<Copyright>Copyright © 2017-2022 by Serraniel</Copyright>
<PackageLicenseUrl></PackageLicenseUrl>
<PackageProjectUrl>https://github.com/Serraniel/SweetLib</PackageProjectUrl>
<RepositoryUrl>https://github.com/Serraniel/SweetLib</RepositoryUrl>
<PackageRequireLicenseAcceptance>true</PackageRequireLicenseAcceptance>
<PackageIconUrl>https://github.com/Serraniel/SweetLib/blob/develop/nuget_icon.png?raw=true</PackageIconUrl>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<PackageLicenseFile>LICENSE</PackageLicenseFile>
<AssemblyVersion>0.1.0.3</AssemblyVersion>
<FileVersion>0.1.0.3</FileVersion>
<LangVersion>latest</LangVersion>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
<DocumentationFile>bin\Release\netstandard1.3\SweetLib.IO.xml</DocumentationFile>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<DocumentationFile>..\Docs\SweetLib.Storage.MySql</DocumentationFile>
<OutputPath></OutputPath>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="MySqlConnector" Version="2.2.5" />
<PackageReference Include="Vsxmd" Version="1.4.5">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>
<ItemGroup>
<None Include="..\LICENSE">
<Pack>True</Pack>
<PackagePath></PackagePath>
</None>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\SweetLib.Storage\SweetLib.Storage.csproj" />
</ItemGroup>
</Project>

View file

@ -0,0 +1,18 @@
<?xml version="1.0"?>
<package >
<metadata>
<id>$id$</id>
<version>$version$-alpha</version>
<title>$title$</title>
<authors>Serraniel</authors>
<owners>$author$</owners>
<licenseUrl>https://opensource.org/licenses/GPL-3.0</licenseUrl>
<projectUrl>https://github.com/Serraniel/SweetLib</projectUrl>
<iconUrl>https://github.com/Serraniel/SweetLib/blob/develop/nuget_icon.png?raw=true</iconUrl>
<requireLicenseAcceptance>true</requireLicenseAcceptance>
<description>$description$</description>
<releaseNotes>First pre version just to try that nuget package thing :)</releaseNotes>
<copyright>Copyright © 2017 Serraniel</copyright>
<tags>Utility</tags>
</metadata>
</package>

View file

@ -0,0 +1,2 @@
nuget pack SweetLib.Storage.MySql.csproj -properties Configuration=Release -symbols
pause

View file

@ -0,0 +1,8 @@
using System.Data.Common;
namespace SweetLib.Storage.Database
{
public interface IDatabaseConnectionProvider : IConnectionProvider<DbConnection>
{
}
}

View file

@ -0,0 +1,39 @@
using System.Collections.Generic;
using System.Data.Common;
using System.Threading.Tasks;
namespace SweetLib.Storage.Database
{
/// <summary>
/// Object to store in database.
/// </summary>
/// <typeparam name="T">Type of the used identifier.</typeparam>
public interface IDatabaseObject<T> : IStorable<T>
{
/// <summary>
/// Assigns field names of the database columns.
/// </summary>
/// <returns></returns>
IEnumerable<string> AssignFieldNames();
/// <summary>
/// Assigns values to the query parameters.
/// </summary>
/// <param name="parameters"></param>
/// <returns></returns>
Task AssignToAsync(DbParameterCollection parameters);
/// <summary>
/// Assigns values to the object.
/// </summary>
/// <param name="reader"></param>
/// <returns></returns>
Task AssignFromAsync(DbDataReader reader);
/// <summary>
/// Table name of the object.
/// </summary>
/// <returns></returns>
string TableName();
}
}

View file

@ -0,0 +1,16 @@
using System.Data;
namespace SweetLib.Storage
{
/// <summary>
/// Provider for database connection.
/// </summary>
public interface IConnectionProvider<out T> where T : IDbConnection
{
/// <summary>
/// Gets an instance of a database connection.
/// </summary>
/// <returns></returns>
T GetConnection();
}
}

View file

@ -0,0 +1,23 @@
using System.Threading.Tasks;
namespace SweetLib.Storage
{
/// <summary>
/// Generates a new object id.
/// </summary>
/// <typeparam name="T">Type of the used identifier.</typeparam>
public interface IIdGenerator<T>
{
/// <summary>
/// Generates a new id of the specified type.
/// </summary>
/// <returns></returns>
T GenerateNewId();
/// <summary>
/// Generates a new id of the specified type. Runs asynchronous.
/// </summary>
/// <returns></returns>
Task<T> GenerateNewIdAsync();
}
}

View file

@ -0,0 +1,20 @@
namespace SweetLib.Storage
{
/// <summary>
/// Interface for identified objects.
/// </summary>
/// <typeparam name="T">Type of the identifier.</typeparam>
public interface IIdentified<T>
{
/// <summary>
/// Identifier of the object.
/// </summary>
T Id { get; set; }
/// <summary>
/// Gets a dummy Id for unidentified instances;
/// </summary>
/// <returns></returns>
T UnidentifiedId();
}
}

View file

@ -0,0 +1,30 @@
using System.Threading.Tasks;
namespace SweetLib.Storage
{
/// <summary>
/// Interface for objects which can be stored and restored.
/// </summary>
/// <typeparam name="T">Type of the used identifier.</typeparam>
public interface IStorable<T> : IIdentified<T>
{
/// <summary>
/// Stores the object.
/// </summary>
/// <returns></returns>
Task Store();
/// <summary>
/// Restores an object.
/// </summary>
/// <param name="identifier">Identifier of the object to restore.</param>
/// <returns></returns>
Task<bool> Restore(T identifier);
/// <summary>
/// Determines whether the current object instance is new or already exists.
/// </summary>
/// <returns>Boolean indicating if the current object instance is new or already exists.</returns>
bool IsNew();
}
}

View file

@ -28,8 +28,10 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="SweetLib" Version="0.2.1-alpha" />
<PackageReference Include="Vsxmd" Version="1.2.0" />
<PackageReference Include="Vsxmd" Version="1.4.5">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>
<ItemGroup>

View file

@ -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