diff options
Diffstat (limited to 'docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud')
6 files changed, 180 insertions, 13 deletions
| diff --git a/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/ColumnInfo.cs b/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/ColumnInfo.cs index c3d118c..1329c99 100644 --- a/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/ColumnInfo.cs +++ b/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/ColumnInfo.cs @@ -1,8 +1,12 @@ +using System.Data;  using System.Reflection;  using System.Text;  namespace CrupestApi.Commons.Crud; +public delegate Task EntityPreSave(object? entity, ColumnInfo column, TableInfo table, IDbConnection connection); +public delegate Task EntityPostGet(object? entity, ColumnInfo column, TableInfo table, IDbConnection connection); +  public class ColumnInfo  {      private Type ExtractRealTypeFromNullable(Type type) @@ -24,7 +28,7 @@ public class ColumnInfo          }          EntityType = entityType; -        PropertyName = null; +        PropertyName = sqlColumnName;          PropertyType = typeof(int);          PropertyRealType = typeof(int);          SqlColumnName = sqlColumnName; @@ -45,36 +49,55 @@ public class ColumnInfo          EntityType = entityType;          PropertyName = entityPropertyName; +        PropertyInfo = entityType.GetProperty(entityPropertyName); -        var property = entityType.GetProperty(entityPropertyName); - -        if (property is null) +        if (PropertyInfo is null)              throw new Exception("Public property with given name does not exist."); -        PropertyType = property.PropertyType; +        PropertyType = PropertyInfo.PropertyType;          PropertyRealType = ExtractRealTypeFromNullable(PropertyType); -        var columnAttribute = property.GetCustomAttribute<ColumnAttribute>(); +        var columnAttribute = PropertyInfo.GetCustomAttribute<ColumnAttribute>();          if (columnAttribute is null)          {              SqlColumnName = PropertyName;              Nullable = true;              IndexType = ColumnIndexType.None; +            DefaultEmptyForString = false;          }          else          {              SqlColumnName = columnAttribute.DatabaseName ?? PropertyName;              Nullable = !columnAttribute.NonNullable;              IndexType = columnAttribute.IndexType; +            DefaultEmptyForString = columnAttribute.DefaultEmptyForString;          }          ColumnTypeInfo = typeRegistry.GetRequiredByDataType(PropertyRealType);          TypeRegistry = typeRegistry; + +        if (DefaultEmptyForString) +        { +            EntityPostGet += (entity, column, _, _) => +            { +                var pi = column.PropertyInfo; +                if (pi is not null && column.ColumnTypeInfo.GetDatabaseType() == typeof(string)) +                { +                    var value = pi.GetValue(entity); +                    if (value is null) +                    { +                        pi.SetValue(entity, string.Empty); +                    } +                } +                return Task.CompletedTask; +            }; +        }      }      public Type EntityType { get; }      // If null, there is no corresponding property. -    public string? PropertyName { get; } +    public PropertyInfo? PropertyInfo { get; } = null; +    public string PropertyName { get; }      public Type PropertyType { get; }      public Type PropertyRealType { get; }      public string SqlColumnName { get; } @@ -84,6 +107,10 @@ public class ColumnInfo      public bool IsPrimaryKey { get; }      public bool IsAutoIncrement { get; }      public ColumnIndexType IndexType { get; } +    public bool DefaultEmptyForString { get; } + +    public event EntityPreSave? EntityPreSave; +    public event EntityPostGet? EntityPostGet;      public string SqlType => TypeRegistry.GetSqlType(ColumnTypeInfo); diff --git a/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/ColumnMetadata.cs b/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/ColumnMetadata.cs index 6f46cd5..c31a13e 100644 --- a/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/ColumnMetadata.cs +++ b/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/ColumnMetadata.cs @@ -28,4 +28,7 @@ public class ColumnAttribute : Attribute, IColumnMetadata      public bool IsAutoIncrement { get; set; }      public ColumnIndexType IndexType { get; set; } = ColumnIndexType.None; + +    // Use empty string for default value of string type. +    public bool DefaultEmptyForString { get; set; }  } diff --git a/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/ColumnTypeInfo.cs b/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/ColumnTypeInfo.cs index ff8ccea..d7adb33 100644 --- a/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/ColumnTypeInfo.cs +++ b/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/ColumnTypeInfo.cs @@ -1,9 +1,16 @@  using System.Diagnostics; +using System.Text.Json; +using System.Text.Json.Serialization;  namespace CrupestApi.Commons.Crud;  public interface IColumnTypeInfo  { +    JsonConverter? GetOptionalJsonConverter() +    { +        return null; +    } +      Type GetDataType();      Type GetDatabaseType();      object ConvertToDatabase(object data); @@ -122,6 +129,46 @@ public abstract class CustomColumnTypeInfo<TDataType, TDatabaseType> : ICustomCo      }  } +public class DateTimeColumnTypeInfo : CustomColumnTypeInfo<DateTime, long> +{ +    private readonly DateTimeJsonConverter _jsonConverter = new DateTimeJsonConverter(); + +    public JsonConverter GetJsonConverter() +    { +        return _jsonConverter; +    } + +    public override long ConvertToDatabase(DateTime data) +    { +        return new DateTimeOffset(data).ToUnixTimeSeconds(); +    } + +    public override DateTime ConvertFromDatabase(long databaseData) +    { +        return DateTimeOffset.FromUnixTimeSeconds(databaseData).LocalDateTime; +    } +} + +public class DateTimeJsonConverter : JsonConverter<DateTime> +{ +    public override bool HandleNull => false; + +    public override bool CanConvert(Type typeToConvert) +    { +        return typeToConvert == typeof(DateTime); +    } + +    public override DateTime Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) +    { +        return DateTimeOffset.FromUnixTimeSeconds(reader.GetInt64()).LocalDateTime; +    } + +    public override void Write(Utf8JsonWriter writer, DateTime value, JsonSerializerOptions options) +    { +        writer.WriteNumberValue(new DateTimeOffset(value).ToUnixTimeSeconds()); +    } +} +  public class ColumnTypeInfoRegistry  {      public static IReadOnlyList<IColumnTypeInfo> BuiltinList = new List<IColumnTypeInfo>() @@ -139,7 +186,7 @@ public class ColumnTypeInfoRegistry      public static IReadOnlyList<IColumnTypeInfo> CustomList = new List<IColumnTypeInfo>()      { -        // TODO: Add custom ones. +        new DateTimeColumnTypeInfo(),      };      public static ColumnTypeInfoRegistry Singleton { get; } @@ -229,4 +276,13 @@ public class ColumnTypeInfoRegistry          }      } +    public IEnumerable<JsonConverter> GetJsonConverters() +    { +        foreach (var columnTypeInfo in _list) +        { +            var converter = columnTypeInfo.GetOptionalJsonConverter(); +            if (converter is not null) +                yield return converter; +        } +    }  } diff --git a/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/CrudService.cs b/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/CrudService.cs index bbd5e9a..7da6ac7 100644 --- a/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/CrudService.cs +++ b/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/CrudService.cs @@ -8,13 +8,13 @@ public class CrudService<TEntity>  {      protected readonly TableInfo _table;      protected readonly IOptionsSnapshot<CrupestApiConfig> _crupestApiOptions; -    protected readonly ILogger<CrudService<TEntity>> _logger; +    private readonly ILogger<CrudService<TEntity>> _logger; -    public CrudService(IOptionsSnapshot<CrupestApiConfig> crupestApiOptions, ILogger<CrudService<TEntity>> logger) +    public CrudService(ServiceProvider services)      {          _table = new TableInfo(typeof(TEntity)); -        _crupestApiOptions = crupestApiOptions; -        _logger = logger; +        _crupestApiOptions = services.GetRequiredService<IOptionsSnapshot<CrupestApiConfig>>(); +        _logger = services.GetRequiredService<ILogger<CrudService<TEntity>>>();      }      public virtual string GetDbConnectionString() @@ -78,4 +78,12 @@ public class CrudService<TEntity>          var sql = _table.GenerateUpdateSql(where, update, out parameters);          return await connection.ExecuteAsync(sql, parameters);      } + +    public virtual async Task<int> DeleteAsync(WhereClause? where) +    { +        var connection = await EnsureDatabase(); +        DynamicParameters parameters; +        var sql = _table.GenerateDeleteSql(where, out parameters); +        return await connection.ExecuteAsync(sql, parameters); +    }  } diff --git a/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/TableInfo.cs b/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/TableInfo.cs index 9610e40..c58897c 100644 --- a/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/TableInfo.cs +++ b/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/TableInfo.cs @@ -190,6 +190,46 @@ CREATE TABLE {tableName}(          return result.ToString();      } +    public InsertClause GenerateInsertClauseFromObject(object value) +    { +        var insertClause = InsertClause.Create(); + +        foreach (var column in ColumnInfos) +        { +            var propertyInfo = column.PropertyInfo; +            if (propertyInfo is null) +            { +                propertyInfo = EntityType.GetProperty(column.PropertyName); +            } +            if (propertyInfo is null) +            { +                if (column.IsAutoIncrement) +                { +                    continue; +                } +                else +                { +                    throw new Exception($"Property {column.PropertyName} not found."); +                } +            } + +            var propertyValue = propertyInfo.GetValue(value); +            if (propertyValue is null) +            { +                if (column.IsAutoIncrement) +                { +                    continue; +                } +                else +                { +                    insertClause.Add(column.SqlColumnName, propertyValue); +                } +            } +        } + +        return insertClause; +    } +      public string GenerateInsertSql(InsertClause insertClause, out DynamicParameters parameters)      {          var relatedColumns = insertClause.GetRelatedColumns(); @@ -244,4 +284,32 @@ CREATE TABLE {tableName}(          return sb.ToString();      } -}
\ No newline at end of file + +    public string GenerateDeleteSql(WhereClause? whereClause, out DynamicParameters parameters) +    { +        if (whereClause is not null) +        { +            var relatedColumns = ((IWhereClause)whereClause).GetRelatedColumns() ?? new List<string>(); +            foreach (var column in relatedColumns) +            { +                if (!ColumnNameList.Contains(column)) +                { +                    throw new ArgumentException($"Column {column} is not in the table."); +                } +            } +        } + +        parameters = new DynamicParameters(); + +        StringBuilder sb = new StringBuilder("DELETE FROM "); +        sb.Append(TableName); +        if (whereClause is not null) +        { +            sb.Append(" WHERE "); +            sb.Append(whereClause.GenerateSql(parameters)); +        } +        sb.Append(';'); + +        return sb.ToString(); +    } +} diff --git a/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/WhereClause.cs b/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/WhereClause.cs index 674ac9d..2352e7e 100644 --- a/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/WhereClause.cs +++ b/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/WhereClause.cs @@ -213,6 +213,11 @@ public class WhereClause : IWhereClause          return new WhereClause(clauses);      } +    public WhereClause Add(string column, string op, object value) +    { +        return Add(CompareWhereClause.Create(column, op, value)); +    } +      public WhereClause Eq(string column, object value)      {          return Add(CompareWhereClause.Eq(column, value)); | 
