aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/ColumnTypeInfo.cs218
-rw-r--r--docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/CrudService.cs21
2 files changed, 221 insertions, 18 deletions
diff --git a/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/ColumnTypeInfo.cs b/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/ColumnTypeInfo.cs
new file mode 100644
index 0000000..02848f3
--- /dev/null
+++ b/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/ColumnTypeInfo.cs
@@ -0,0 +1,218 @@
+using System.Diagnostics;
+
+namespace CrupestApi.Commons;
+
+public interface IColumnTypeInfo
+{
+ Type GetDataType();
+ Type GetDatabaseType();
+ object ConvertToDatabase(object data);
+ object ConvertFromDatabase(object databaseData);
+
+ void Validate(IReadOnlyDictionary<Type, IColumnTypeInfo> typeInfoMap)
+ {
+ var typeSet = new HashSet<Type>();
+
+ IColumnTypeInfo current = this;
+
+ while (!typeof(BuiltinColumnTypeInfo<>).IsInstanceOfType(current))
+ {
+ var dataType = GetDataType();
+
+ if (typeSet.Contains(dataType))
+ {
+ throw new Exception("Circular reference detected.");
+ }
+ typeSet.Add(dataType);
+
+ var databaseType = GetDatabaseType();
+ if (!typeInfoMap.ContainsKey(databaseType))
+ {
+ throw new Exception("Broken type chain.");
+ }
+
+ current = typeInfoMap[databaseType];
+ }
+ }
+}
+
+public interface IBuiltinColumnTypeInfo : IColumnTypeInfo
+{
+ string GetSqlType();
+}
+
+public class BuiltinColumnTypeInfo<T> : IBuiltinColumnTypeInfo
+{
+ private readonly string _sqlType;
+
+ public BuiltinColumnTypeInfo(string sqlType)
+ {
+ _sqlType = sqlType;
+ }
+
+ public Type GetDataType()
+ {
+ return typeof(T);
+ }
+
+ public Type GetDatabaseType()
+ {
+ return typeof(T);
+ }
+
+ public string GetSqlType()
+ {
+ return _sqlType;
+ }
+
+ public T ConvertToDatabase(T data)
+ {
+ return data;
+ }
+
+ public T ConvertFromDatabase(T databaseData)
+ {
+ return databaseData;
+ }
+
+ object IColumnTypeInfo.ConvertToDatabase(object data)
+ {
+ return data;
+ }
+
+ object IColumnTypeInfo.ConvertFromDatabase(object databaseData)
+ {
+ return databaseData;
+ }
+}
+
+public abstract class CustomColumnTypeInfo<TDataType, TDatabaseType> : IColumnTypeInfo
+ where TDataType : notnull where TDatabaseType : notnull
+{
+
+ public Type GetDataType()
+ {
+ return typeof(TDataType);
+ }
+
+ public Type GetDatabaseType()
+ {
+ return typeof(TDatabaseType);
+ }
+
+ public abstract TDatabaseType ConvertToDatabase(TDataType data);
+ public abstract TDataType ConvertFromDatabase(TDatabaseType databaseData);
+
+ object IColumnTypeInfo.ConvertToDatabase(object data)
+ {
+ Debug.Assert(typeof(TDataType).IsInstanceOfType(data));
+ return ConvertToDatabase((TDataType)data);
+ }
+
+ object IColumnTypeInfo.ConvertFromDatabase(object databaseData)
+ {
+ Debug.Assert(typeof(TDatabaseType).IsInstanceOfType(databaseData));
+ return ConvertFromDatabase((TDatabaseType)databaseData);
+ }
+}
+
+public class ColumnTypeInfoRegistry
+{
+ public static IReadOnlyList<IColumnTypeInfo> BuiltinList = new List<IColumnTypeInfo>()
+ {
+ new BuiltinColumnTypeInfo<char>("INTEGER"),
+ new BuiltinColumnTypeInfo<short>("INTEGER"),
+ new BuiltinColumnTypeInfo<int>("INTEGER"),
+ new BuiltinColumnTypeInfo<long>("INTEGER"),
+ new BuiltinColumnTypeInfo<float>("REAL"),
+ new BuiltinColumnTypeInfo<double>("REAL"),
+ new BuiltinColumnTypeInfo<string>("TEXT"),
+ new BuiltinColumnTypeInfo<byte[]>("BLOB"),
+ };
+
+
+ public static IReadOnlyList<IColumnTypeInfo> CustomList = new List<IColumnTypeInfo>()
+ {
+ // TODO: Add custom ones.
+ };
+
+ public static ColumnTypeInfoRegistry Singleton { get; }
+
+ static ColumnTypeInfoRegistry()
+ {
+ Singleton = new ColumnTypeInfoRegistry();
+
+ foreach (var builtinColumnTypeInfo in BuiltinList)
+ {
+ Singleton.Register(builtinColumnTypeInfo);
+ }
+
+ foreach (var customColumnTypeInfo in CustomList)
+ {
+ Singleton.Register(customColumnTypeInfo);
+ }
+
+ Singleton.Validate();
+ }
+
+
+
+ private readonly List<IColumnTypeInfo> _list;
+ private readonly Dictionary<Type, IColumnTypeInfo> _map;
+ private bool _dirty = false;
+
+ public ColumnTypeInfoRegistry()
+ {
+ _list = new List<IColumnTypeInfo>();
+ _map = new Dictionary<Type, IColumnTypeInfo>();
+ }
+
+ public void Register(IColumnTypeInfo columnTypeInfo)
+ {
+ Debug.Assert(!_list.Contains(columnTypeInfo));
+ _list.Add(columnTypeInfo);
+ _map.Add(columnTypeInfo.GetDataType(), columnTypeInfo);
+ _dirty = true;
+ }
+
+ public IColumnTypeInfo? GetByDataType(Type type)
+ {
+ return _map.GetValueOrDefault(type);
+ }
+
+ public string GetSqlType(Type type)
+ {
+ EnsureValidity();
+
+ IColumnTypeInfo? current = GetByDataType(type);
+ if (current is null)
+ {
+ throw new Exception("Unsupported type for sql.");
+ }
+
+ while (!typeof(IBuiltinColumnTypeInfo).IsInstanceOfType(current))
+ {
+ current = GetByDataType(current.GetDatabaseType());
+ Debug.Assert(current is not null);
+ }
+
+ return ((IBuiltinColumnTypeInfo)current).GetSqlType();
+ }
+
+ public void Validate()
+ {
+ foreach (var columnTypeInfo in _list)
+ {
+ columnTypeInfo.Validate(_map);
+ }
+ }
+
+ public void EnsureValidity()
+ {
+ if (_dirty)
+ {
+ Validate();
+ }
+ }
+
+}
diff --git a/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/CrudService.cs b/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/CrudService.cs
index d5edd13..bc4ed50 100644
--- a/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/CrudService.cs
+++ b/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/CrudService.cs
@@ -60,30 +60,15 @@ public class CrudService<TEntity>
}
}
- public string GetDatabaseTypeName(Type type)
+ public string GetSqlType(Type type)
{
- if (type == typeof(int))
- {
- return "INTEGER";
- }
- else if (type == typeof(double))
- {
- return "REAL";
- }
- else if (type == typeof(bool))
- {
- return "BOOLEAN";
- }
- else
- {
- throw new DatabaseInternalException($"Type {type} is not supported.");
- }
+ return ColumnTypeInfoRegistry.Singleton.GetSqlType(type);
}
public string GetCreateTableColumnSql()
{
var properties = typeof(TEntity).GetProperties();
- var sql = string.Join(", ", properties.Select(p => $"{p.Name} {GetDatabaseTypeName(p.PropertyType)}"));
+ var sql = string.Join(", ", properties.Select(p => $"{p.Name} {GetSqlType(p.PropertyType)}"));
return sql;
}