diff options
author | crupest <crupest@outlook.com> | 2022-12-06 21:03:36 +0800 |
---|---|---|
committer | crupest <crupest@outlook.com> | 2022-12-20 20:32:52 +0800 |
commit | 1870bc78d4a2733246322c5540761da852afe713 (patch) | |
tree | 7dd05378a17e6b687af4987fe50e425171f0e07b | |
parent | c16159d9ef9e3b3f359966d17f3aa6eb420620f6 (diff) | |
download | crupest-1870bc78d4a2733246322c5540761da852afe713.tar.gz crupest-1870bc78d4a2733246322c5540761da852afe713.tar.bz2 crupest-1870bc78d4a2733246322c5540761da852afe713.zip |
Develop secret api. v16
5 files changed, 134 insertions, 235 deletions
diff --git a/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/ColumnInfo.cs b/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/ColumnInfo.cs index 1754c8d..081071f 100644 --- a/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/ColumnInfo.cs +++ b/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/ColumnInfo.cs @@ -20,11 +20,11 @@ public class ColumnInfo } // A column with no property. - public ColumnInfo(Type entityType, string sqlColumnName, bool isPrimaryKey, bool isAutoIncrement, IColumnTypeInfo typeInfo, ColumnIndexType indexType = ColumnIndexType.None, ColumnTypeInfoRegistry? typeRegistry = null) + public ColumnInfo(Type entityType, string sqlColumnName, bool isPrimaryKey, bool isAutoIncrement, ColumnTypeInfo typeInfo, ColumnIndexType indexType = ColumnIndexType.None, ColumnTypeRegistry? typeRegistry = null) { if (typeRegistry is null) { - typeRegistry = ColumnTypeInfoRegistry.Singleton; + typeRegistry = ColumnTypeRegistry.Instance; } EntityType = entityType; @@ -40,11 +40,11 @@ public class ColumnInfo IndexType = indexType; } - public ColumnInfo(Type entityType, string entityPropertyName, ColumnTypeInfoRegistry? typeRegistry = null) + public ColumnInfo(Type entityType, string entityPropertyName, ColumnTypeRegistry? typeRegistry = null) { if (typeRegistry is null) { - typeRegistry = ColumnTypeInfoRegistry.Singleton; + typeRegistry = ColumnTypeRegistry.Instance; } EntityType = entityType; @@ -73,25 +73,8 @@ public class ColumnInfo DefaultEmptyForString = columnAttribute.DefaultEmptyForString; } - ColumnTypeInfo = typeRegistry.GetRequiredByDataType(PropertyRealType); + ColumnTypeInfo = typeRegistry.GetRequired(PropertyRealType); TypeRegistry = typeRegistry; - - if (DefaultEmptyForString) - { - EntityPostGet += (entity, column, _, _) => - { - var pi = column.PropertyInfo; - if (pi is not null && column.ColumnTypeInfo.GetUnderlineType() == typeof(string)) - { - var value = pi.GetValue(entity); - if (value is null) - { - pi.SetValue(entity, string.Empty); - } - } - return Task.CompletedTask; - }; - } } public Type EntityType { get; } @@ -101,18 +84,17 @@ public class ColumnInfo public Type PropertyType { get; } public Type PropertyRealType { get; } public string SqlColumnName { get; } - public ColumnTypeInfoRegistry TypeRegistry { get; set; } - public IColumnTypeInfo ColumnTypeInfo { get; } + public ColumnTypeRegistry TypeRegistry { get; set; } + public ColumnTypeInfo ColumnTypeInfo { get; } public bool Nullable { get; } public bool IsPrimaryKey { get; } public bool IsAutoIncrement { get; } public ColumnIndexType IndexType { get; } - public bool DefaultEmptyForString { get; } - public event EntityPreSave? EntityPreSave; - public event EntityPostGet? EntityPostGet; + // TODO: Implement this behavior. + public bool DefaultEmptyForString { get; } - public string SqlType => TypeRegistry.GetSqlTypeRecursive(ColumnTypeInfo); + public string SqlType => ColumnTypeInfo.SqlType; public string GenerateCreateTableColumnString() { diff --git a/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/ColumnTypeInfo.cs b/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/ColumnTypeInfo.cs index e39be98..4e640ff 100644 --- a/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/ColumnTypeInfo.cs +++ b/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/ColumnTypeInfo.cs @@ -1,3 +1,4 @@ +using System.Collections.Generic; using System.Data; using System.Diagnostics; using System.Text.Json; @@ -5,157 +6,148 @@ using System.Text.Json.Serialization; namespace CrupestApi.Commons.Crud; -public interface IColumnTypeInfo +/// <summary> Represents a type of one column. </summary> +public abstract class ColumnTypeInfo { - JsonConverter? GetOptionalJsonConverter() + protected ColumnTypeInfo(Type supportedType) { - return null; + SupportedType = supportedType; } - Type GetPropertyType(); - Type GetUnderlineType(); - object ConvertToUnderline(object data); - object ConvertFromUnderline(object databaseData); + public Type SupportedType { get; } - void Validate(IReadOnlyDictionary<Type, IColumnTypeInfo> typeInfoMap) + public bool IsOfSupportedType(object value) { - var typeSet = new HashSet<Type>(); + return value is not null && SupportedType.IsAssignableFrom(value.GetType()); + } - IColumnTypeInfo current = this; + public abstract BasicColumnTypeInfo UnderlineType { get; } - while (current is not IBuiltinColumnTypeInfo) - { - var dataType = GetPropertyType(); + public abstract IReadOnlyList<DerivedColumnTypeInfo> DerivedTypes { get; } - if (typeSet.Contains(dataType)) - { - throw new Exception("Circular reference detected."); - } - typeSet.Add(dataType); + public abstract string SqlType { get; } - var databaseType = GetUnderlineType(); - if (!typeInfoMap.ContainsKey(databaseType)) - { - throw new Exception("Broken type chain."); - } + public abstract DbType DbType { get; } - current = typeInfoMap[databaseType]; - } - } -} + /// <summary> + /// An optional json converter for this type. + /// </summary> + /// <returns>The converter if this type needs a json converter. Otherwise null.</returns> + public abstract JsonConverter? GetJsonConverter(); -public interface IBuiltinColumnTypeInfo : IColumnTypeInfo -{ - /// To use for non-builtin type, use <see cref="ColumnTypeInfoRegistry.GetSqlTypeRecursive(IColumnTypeInfo)" /> because we need registry to query more information. - string GetSqlType(); - // To use for non-builtin type, use <see cref="ColumnTypeInfoRegistry.GetDbType(IColumnTypeInfo)" /> because we need registry to query more information. - DbType GetDbType(); + /// <summary> + /// Convert a value into underline type. + /// </summary> + public abstract object? ConvertToUnderline(object? value); + + /// <summary> + /// Convert to a value of this type from value of underline type. + /// </summary> + public abstract object? ConvertFromUnderline(object? underlineValue); } -public class BuiltinColumnTypeInfo<T> : IBuiltinColumnTypeInfo +public class BasicColumnTypeInfo : ColumnTypeInfo { + public static BasicColumnTypeInfo<char> CharColumnTypeInfo { get; } = new BasicColumnTypeInfo<char>("INTEGER", DbType.Int32); + public static BasicColumnTypeInfo<short> ShortColumnTypeInfo { get; } = new BasicColumnTypeInfo<short>("INTEGER", DbType.Int32); + public static BasicColumnTypeInfo<int> IntColumnTypeInfo { get; } = new BasicColumnTypeInfo<int>("INTEGER", DbType.Int32); + public static BasicColumnTypeInfo<long> LongColumnTypeInfo { get; } = new BasicColumnTypeInfo<long>("INTEGER", DbType.Int64); + public static BasicColumnTypeInfo<float> FloatColumnTypeInfo { get; } = new BasicColumnTypeInfo<float>("REAL", DbType.Double); + public static BasicColumnTypeInfo<double> DoubleColumnTypeInfo { get; } = new BasicColumnTypeInfo<double>("REAL", DbType.Double); + public static BasicColumnTypeInfo<string> StringColumnTypeInfo { get; } = new BasicColumnTypeInfo<string>("TEXT", DbType.String); + public static BasicColumnTypeInfo<byte[]> ByteColumnTypeInfo { get; } = new BasicColumnTypeInfo<byte[]>("BLOB", DbType.Binary); + private readonly string _sqlType; private readonly DbType _dbType; + internal List<DerivedColumnTypeInfo> _derivedTypes = new List<DerivedColumnTypeInfo>(); - public BuiltinColumnTypeInfo(string sqlType, DbType dbType) + public BasicColumnTypeInfo(Type type, string sqlType, DbType dbType) + : base(type) { _sqlType = sqlType; _dbType = dbType; } - public Type GetPropertyType() - { - return typeof(T); - } + public override BasicColumnTypeInfo UnderlineType => this; - public Type GetUnderlineType() - { - return typeof(T); - } - - public string GetSqlType() - { - return _sqlType; - } + public override IReadOnlyList<DerivedColumnTypeInfo> DerivedTypes => _derivedTypes; - public DbType GetDbType() - { - return _dbType; - } + public override string SqlType => _sqlType; - public T ConvertToDatabase(T data) - { - return data; - } + public override DbType DbType => _dbType; - public T ConvertFromDatabase(T databaseData) + public override object? ConvertToUnderline(object? value) { - return databaseData; + Debug.Assert(value is null || SupportedType.IsInstanceOfType(value)); + return value; } - object IColumnTypeInfo.ConvertToUnderline(object data) + public override object? ConvertFromUnderline(object? underlineValue) { - return data; + Debug.Assert(underlineValue is null || SupportedType.IsInstanceOfType(underlineValue)); + return underlineValue; } - object IColumnTypeInfo.ConvertFromUnderline(object databaseData) + public override JsonConverter? GetJsonConverter() { - return databaseData; + return null; } } -public interface ICustomColumnTypeInfo : IColumnTypeInfo +public class BasicColumnTypeInfo<T> : BasicColumnTypeInfo { - + public BasicColumnTypeInfo(string sqlType, DbType dbType) : base(typeof(T), sqlType, dbType) { } } -public abstract class CustomColumnTypeInfo<TPropertyType, TUnderlineType> : ICustomColumnTypeInfo - where TPropertyType : notnull where TUnderlineType : notnull +public abstract class DerivedColumnTypeInfo : ColumnTypeInfo { - - public Type GetPropertyType() + protected DerivedColumnTypeInfo(Type supportedType, BasicColumnTypeInfo underlineType) + : base(supportedType) { - return typeof(TPropertyType); + UnderlineType = underlineType; + UnderlineType._derivedTypes.Add(this); } - public Type GetUnderlineType() - { - return typeof(TUnderlineType); - } + public override BasicColumnTypeInfo UnderlineType { get; } - public abstract TUnderlineType ConvertToUnderline(TPropertyType data); - public abstract TPropertyType ConvertFromUnderline(TUnderlineType databaseData); + private static readonly List<DerivedColumnTypeInfo> _emptyList = new List<DerivedColumnTypeInfo>(); - object IColumnTypeInfo.ConvertToUnderline(object data) - { - Debug.Assert(data is TPropertyType); - return ConvertToUnderline((TPropertyType)data); - } + public override IReadOnlyList<DerivedColumnTypeInfo> DerivedTypes => _emptyList; - object IColumnTypeInfo.ConvertFromUnderline(object databaseData) - { - Debug.Assert(databaseData is TUnderlineType); - return ConvertFromUnderline((TUnderlineType)databaseData); - } + public override string SqlType => UnderlineType!.SqlType; + + public override DbType DbType => UnderlineType!.DbType; } -public class DateTimeColumnTypeInfo : CustomColumnTypeInfo<DateTime, long> +public class DateTimeColumnTypeInfo : DerivedColumnTypeInfo { private readonly DateTimeJsonConverter _jsonConverter = new DateTimeJsonConverter(); - public JsonConverter GetJsonConverter() + public DateTimeColumnTypeInfo() + : base(typeof(DateTime), BasicColumnTypeInfo.LongColumnTypeInfo) + { + + } + + public override JsonConverter GetJsonConverter() { return _jsonConverter; } - public override long ConvertToUnderline(DateTime data) + public override object? ConvertToUnderline(object? value) { - return new DateTimeOffset(data).ToUnixTimeSeconds(); + if (value is null) return null; + + Debug.Assert(value is DateTime); + return new DateTimeOffset((DateTime)value).ToUnixTimeSeconds(); } - public override DateTime ConvertFromUnderline(long databaseData) + public override object? ConvertFromUnderline(object? underlineValue) { - return DateTimeOffset.FromUnixTimeSeconds(databaseData).LocalDateTime; + if (underlineValue is null) return null; + + Debug.Assert(typeof(long).IsAssignableFrom(underlineValue.GetType())); + return DateTimeOffset.FromUnixTimeSeconds((long)underlineValue).LocalDateTime; } } @@ -179,162 +171,87 @@ public class DateTimeJsonConverter : JsonConverter<DateTime> } } -public class ColumnTypeInfoRegistry +public class ColumnTypeRegistry { - public static IReadOnlyList<IColumnTypeInfo> BuiltinList = new List<IColumnTypeInfo>() - { - new BuiltinColumnTypeInfo<char>("INTEGER", DbType.Int32), - new BuiltinColumnTypeInfo<short>("INTEGER", DbType.Int32), - new BuiltinColumnTypeInfo<int>("INTEGER", DbType.Int32), - new BuiltinColumnTypeInfo<long>("INTEGER", DbType.Int64), - new BuiltinColumnTypeInfo<float>("REAL", DbType.Double), - new BuiltinColumnTypeInfo<double>("REAL", DbType.Double), - new BuiltinColumnTypeInfo<string>("TEXT", DbType.String), - new BuiltinColumnTypeInfo<byte[]>("BLOB", DbType.Binary), - }; - - - public static IReadOnlyList<IColumnTypeInfo> CustomList = new List<IColumnTypeInfo>() - { - new DateTimeColumnTypeInfo(), + public static IReadOnlyList<BasicColumnTypeInfo> BasicTypeList = new List<BasicColumnTypeInfo>() + { + BasicColumnTypeInfo.CharColumnTypeInfo, + BasicColumnTypeInfo.ShortColumnTypeInfo, + BasicColumnTypeInfo.IntColumnTypeInfo, + BasicColumnTypeInfo.LongColumnTypeInfo, + BasicColumnTypeInfo.FloatColumnTypeInfo, + BasicColumnTypeInfo.DoubleColumnTypeInfo, + BasicColumnTypeInfo.StringColumnTypeInfo, + BasicColumnTypeInfo.ByteColumnTypeInfo, }; - public static ColumnTypeInfoRegistry Singleton { get; } + public static ColumnTypeRegistry Instance { get; } - static ColumnTypeInfoRegistry() + static ColumnTypeRegistry() { - Singleton = new ColumnTypeInfoRegistry(); - - foreach (var builtinColumnTypeInfo in BuiltinList) - { - Singleton.Register(builtinColumnTypeInfo); - } + Instance = new ColumnTypeRegistry(); - foreach (var customColumnTypeInfo in CustomList) + foreach (var basicColumnTypeInfo in BasicTypeList) { - Singleton.Register(customColumnTypeInfo); + Instance.Register(basicColumnTypeInfo); } - Singleton.Validate(); + Instance.Register(new DateTimeColumnTypeInfo()); } - private readonly List<IColumnTypeInfo> _list; - private readonly Dictionary<Type, IColumnTypeInfo> _map; - private bool _dirty = false; + private readonly List<ColumnTypeInfo> _list; + private readonly Dictionary<Type, ColumnTypeInfo> _map; - public ColumnTypeInfoRegistry() + public ColumnTypeRegistry() { - _list = new List<IColumnTypeInfo>(); - _map = new Dictionary<Type, IColumnTypeInfo>(); + _list = new List<ColumnTypeInfo>(); + _map = new Dictionary<Type, ColumnTypeInfo>(); } - public void Register(IColumnTypeInfo columnTypeInfo) + public void Register(ColumnTypeInfo columnTypeInfo) { Debug.Assert(!_list.Contains(columnTypeInfo)); + Debug.Assert(!_map.ContainsKey(columnTypeInfo.SupportedType)); _list.Add(columnTypeInfo); - _map.Add(columnTypeInfo.GetPropertyType(), columnTypeInfo); - _dirty = true; + _map.Add(columnTypeInfo.SupportedType, columnTypeInfo); } - public IColumnTypeInfo? GetByDataType(Type type) + public ColumnTypeInfo? Get(Type type) { return _map.GetValueOrDefault(type); } - public IColumnTypeInfo GetRequiredByDataType(Type type) + public ColumnTypeInfo? Get<T>() { - return GetByDataType(type) ?? throw new Exception("Unsupported type."); + return Get(typeof(T)); } - public string GetSqlTypeRecursive(IColumnTypeInfo columnTypeInfo) + public ColumnTypeInfo GetRequired(Type type) { - EnsureValidity(); - - IColumnTypeInfo? current = columnTypeInfo; - while (current is not IBuiltinColumnTypeInfo) - { - current = GetByDataType(current.GetUnderlineType()); - Debug.Assert(current is not null); - } - - return ((IBuiltinColumnTypeInfo)current).GetSqlType(); + return Get(type) ?? throw new Exception("Unsupported type."); } - public DbType GetDbTypeRecursive(IColumnTypeInfo columnTypeInfo) + public ColumnTypeInfo GetRequired<T>() { - EnsureValidity(); - - IColumnTypeInfo? current = columnTypeInfo; - if (current is not IBuiltinColumnTypeInfo) - { - current = GetByDataType(current.GetUnderlineType()); - Debug.Assert(current is not null); - } - - return ((IBuiltinColumnTypeInfo)current).GetDbType(); - } - - public object? ConvertToUnderlineRecursive(object? value) - { - EnsureValidity(); - - if (value is null) - { - return null; - } - - IColumnTypeInfo? current = GetByDataType(value.GetType()); - if (current is null) - { - return value; - } - - while (current is not IBuiltinColumnTypeInfo) - { - value = current.ConvertToUnderline(value); - current = GetByDataType(current.GetUnderlineType()); - Debug.Assert(current is not null); - } - - return value; - } - - public string GetSqlType(Type type) - { - return GetSqlTypeRecursive(GetRequiredByDataType(type)); - } - - public DbType GetDbType(Type type) - { - return GetDbTypeRecursive(GetRequiredByDataType(type)); + return GetRequired(typeof(T)); } public object? ConvertToUnderline(object? value) { - return ConvertToUnderlineRecursive(value); - } + if (value is null) return null; - public void Validate() - { - foreach (var columnTypeInfo in _list) - { - columnTypeInfo.Validate(_map); - } - } + var type = value.GetType(); + var columnTypeInfo = Get(type); + if (columnTypeInfo is null) throw new Exception("Unsupported type."); - public void EnsureValidity() - { - if (_dirty) - { - Validate(); - } + return columnTypeInfo.ConvertToUnderline(value); } public IEnumerable<JsonConverter> GetJsonConverters() { foreach (var columnTypeInfo in _list) { - var converter = columnTypeInfo.GetOptionalJsonConverter(); + var converter = columnTypeInfo.GetJsonConverter(); if (converter is not null) yield return converter; } diff --git a/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/TableInfo.cs b/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/TableInfo.cs index c58897c..ac02226 100644 --- a/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/TableInfo.cs +++ b/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/TableInfo.cs @@ -42,7 +42,7 @@ public class TableInfo if (!hasPrimaryKey) { if (hasId) throw new Exception("A column named id already exists but is not primary key."); - var columnInfo = new ColumnInfo(entityType, "id", true, true, ColumnTypeInfoRegistry.Singleton.GetRequiredByDataType(typeof(int))); + var columnInfo = new ColumnInfo(entityType, "id", true, true, ColumnTypeRegistry.Instance.GetRequired<int>()); columnInfos.Add(columnInfo); } diff --git a/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/WhereClause.cs b/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/WhereClause.cs index ba1a28c..26dc306 100644 --- a/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/WhereClause.cs +++ b/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/WhereClause.cs @@ -57,7 +57,7 @@ public static class DynamicParametersExtensions public static string AddRandomNameParameter(this DynamicParameters parameters, object? value) { var parameterName = IWhereClause.GenerateRandomParameterName(parameters); - parameters.Add(parameterName, ColumnTypeInfoRegistry.Singleton.ConvertToUnderlineRecursive(value)); + parameters.Add(parameterName, ColumnTypeRegistry.Instance.ConvertToUnderline(value)); return parameterName; } } diff --git a/docker/crupest-api/CrupestApi/CrupestApi.Commons/Json.cs b/docker/crupest-api/CrupestApi/CrupestApi.Commons/Json.cs index bbb6efe..76746b4 100644 --- a/docker/crupest-api/CrupestApi/CrupestApi.Commons/Json.cs +++ b/docker/crupest-api/CrupestApi/CrupestApi.Commons/Json.cs @@ -14,7 +14,7 @@ public static class CrupestApiJsonExtensions config.AllowTrailingCommas = true; config.PropertyNameCaseInsensitive = true; config.PropertyNamingPolicy = JsonNamingPolicy.CamelCase; - foreach (var converter in ColumnTypeInfoRegistry.Singleton.GetJsonConverters()) + foreach (var converter in ColumnTypeRegistry.Instance.GetJsonConverters()) { config.Converters.Add(converter); } |