aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorcrupest <crupest@outlook.com>2022-12-05 13:50:52 +0800
committercrupest <crupest@outlook.com>2022-12-20 20:32:52 +0800
commit9f7162f12944cc722dba9e91f4d30aa312142d49 (patch)
treeb6e68cfecdfcdb25b240277d667d26eb1d1b6806
parent2d63ac857ed1082f6f7d365674aa734c582a99dd (diff)
downloadcrupest-9f7162f12944cc722dba9e91f4d30aa312142d49.tar.gz
crupest-9f7162f12944cc722dba9e91f4d30aa312142d49.tar.bz2
crupest-9f7162f12944cc722dba9e91f4d30aa312142d49.zip
Develop secret api. v7
-rw-r--r--docker/crupest-api/CrupestApi/CrupestApi.Commons.Tests/CrupestApi.Commons.Tests.csproj28
-rw-r--r--docker/crupest-api/CrupestApi/CrupestApi.Commons.Tests/Usings.cs1
-rw-r--r--docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/ColumnInfo.cs69
-rw-r--r--docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/ColumnMetadata.cs21
-rw-r--r--docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/ColumnTypeInfo.cs24
-rw-r--r--docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/TableInfo.cs73
-rw-r--r--docker/crupest-api/CrupestApi/CrupestApi.sln6
7 files changed, 214 insertions, 8 deletions
diff --git a/docker/crupest-api/CrupestApi/CrupestApi.Commons.Tests/CrupestApi.Commons.Tests.csproj b/docker/crupest-api/CrupestApi/CrupestApi.Commons.Tests/CrupestApi.Commons.Tests.csproj
new file mode 100644
index 0000000..59edf0d
--- /dev/null
+++ b/docker/crupest-api/CrupestApi/CrupestApi.Commons.Tests/CrupestApi.Commons.Tests.csproj
@@ -0,0 +1,28 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+ <PropertyGroup>
+ <TargetFramework>net7.0</TargetFramework>
+ <ImplicitUsings>enable</ImplicitUsings>
+ <Nullable>enable</Nullable>
+
+ <IsPackable>false</IsPackable>
+ </PropertyGroup>
+
+ <ItemGroup>
+ <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.4.0" />
+ <PackageReference Include="xunit" Version="2.4.2" />
+ <PackageReference Include="xunit.runner.visualstudio" Version="2.4.5">
+ <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
+ <PrivateAssets>all</PrivateAssets>
+ </PackageReference>
+ <PackageReference Include="coverlet.collector" Version="3.2.0">
+ <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
+ <PrivateAssets>all</PrivateAssets>
+ </PackageReference>
+ </ItemGroup>
+
+ <ItemGroup>
+ <ProjectReference Include="..\CrupestApi.Commons\CrupestApi.Commons.csproj" />
+ </ItemGroup>
+
+</Project> \ No newline at end of file
diff --git a/docker/crupest-api/CrupestApi/CrupestApi.Commons.Tests/Usings.cs b/docker/crupest-api/CrupestApi/CrupestApi.Commons.Tests/Usings.cs
new file mode 100644
index 0000000..8c927eb
--- /dev/null
+++ b/docker/crupest-api/CrupestApi/CrupestApi.Commons.Tests/Usings.cs
@@ -0,0 +1 @@
+global using Xunit; \ No newline at end of file
diff --git a/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/ColumnInfo.cs b/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/ColumnInfo.cs
new file mode 100644
index 0000000..8b4607d
--- /dev/null
+++ b/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/ColumnInfo.cs
@@ -0,0 +1,69 @@
+using System.Reflection;
+
+namespace CrupestApi.Commons.Crud;
+
+public class ColumnInfo
+{
+ private Type ExtractRealTypeFromNullable(Type type)
+ {
+ if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>))
+ {
+ return type.GetGenericArguments()[0];
+ }
+
+ return type;
+ }
+
+ // A column with no property.
+ public ColumnInfo(Type entityType, string sqlColumnName, bool isPrimaryKey, bool isAutoIncrement, IColumnTypeInfo typeInfo)
+ {
+ EntityType = entityType;
+ PropertyName = null;
+ PropertyType = typeof(int);
+ PropertyRealType = typeof(int);
+ SqlColumnName = sqlColumnName;
+ ColumnTypeInfo = typeInfo;
+ Nullable = false;
+ IsPrimaryKey = isPrimaryKey;
+ IsAutoIncrement = isAutoIncrement;
+ }
+
+ public ColumnInfo(Type entityType, string entityPropertyName)
+ {
+ EntityType = entityType;
+ PropertyName = entityPropertyName;
+
+ var property = entityType.GetProperty(entityPropertyName);
+
+ if (property is null)
+ throw new Exception("Public property with given name does not exist.");
+
+ PropertyType = property.PropertyType;
+ PropertyRealType = ExtractRealTypeFromNullable(PropertyType);
+
+ var columnAttribute = property.GetCustomAttribute<ColumnAttribute>();
+ if (columnAttribute is null)
+ {
+ SqlColumnName = PropertyName;
+ Nullable = true;
+ }
+ else
+ {
+ SqlColumnName = columnAttribute.DatabaseName ?? PropertyName;
+ Nullable = !columnAttribute.NonNullable;
+ }
+ ColumnTypeInfo = ColumnTypeInfoRegistry.Singleton.GetRequiredByDataType(PropertyRealType);
+
+ }
+
+ public Type EntityType { get; }
+ // If null, there is no corresponding property.
+ public string? PropertyName { get; }
+ public Type PropertyType { get; }
+ public Type PropertyRealType { get; }
+ public string SqlColumnName { get; }
+ public IColumnTypeInfo ColumnTypeInfo { get; }
+ public bool Nullable { get; }
+ public bool IsPrimaryKey { get; }
+ public bool IsAutoIncrement { get; }
+} \ No newline at end of file
diff --git a/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/ColumnMetadata.cs b/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/ColumnMetadata.cs
new file mode 100644
index 0000000..cbf38f7
--- /dev/null
+++ b/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/ColumnMetadata.cs
@@ -0,0 +1,21 @@
+namespace CrupestApi.Commons.Crud;
+
+public interface IColumnMetadata
+{
+
+}
+
+[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
+public class ColumnAttribute : Attribute, IColumnMetadata
+{
+ // if null, use the property name.
+ public string? DatabaseName { get; set; }
+
+ // default false.
+ public bool NonNullable { get; set; }
+
+ // default false
+ public bool IsPrimaryKey { get; set; }
+
+ public bool IsAutoIncrement { 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 02848f3..1bdc9c0 100644
--- a/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/ColumnTypeInfo.cs
+++ b/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/ColumnTypeInfo.cs
@@ -1,6 +1,6 @@
using System.Diagnostics;
-namespace CrupestApi.Commons;
+namespace CrupestApi.Commons.Crud;
public interface IColumnTypeInfo
{
@@ -15,7 +15,7 @@ public interface IColumnTypeInfo
IColumnTypeInfo current = this;
- while (!typeof(BuiltinColumnTypeInfo<>).IsInstanceOfType(current))
+ while (current is not IBuiltinColumnTypeInfo)
{
var dataType = GetDataType();
@@ -86,7 +86,12 @@ public class BuiltinColumnTypeInfo<T> : IBuiltinColumnTypeInfo
}
}
-public abstract class CustomColumnTypeInfo<TDataType, TDatabaseType> : IColumnTypeInfo
+public interface ICustomColumnTypeInfo : IColumnTypeInfo
+{
+
+}
+
+public abstract class CustomColumnTypeInfo<TDataType, TDatabaseType> : ICustomColumnTypeInfo
where TDataType : notnull where TDatabaseType : notnull
{
@@ -105,13 +110,13 @@ public abstract class CustomColumnTypeInfo<TDataType, TDatabaseType> : IColumnTy
object IColumnTypeInfo.ConvertToDatabase(object data)
{
- Debug.Assert(typeof(TDataType).IsInstanceOfType(data));
+ Debug.Assert(data is TDataType);
return ConvertToDatabase((TDataType)data);
}
object IColumnTypeInfo.ConvertFromDatabase(object databaseData)
{
- Debug.Assert(typeof(TDatabaseType).IsInstanceOfType(databaseData));
+ Debug.Assert(databaseData is TDatabaseType);
return ConvertFromDatabase((TDatabaseType)databaseData);
}
}
@@ -155,8 +160,6 @@ public class ColumnTypeInfoRegistry
Singleton.Validate();
}
-
-
private readonly List<IColumnTypeInfo> _list;
private readonly Dictionary<Type, IColumnTypeInfo> _map;
private bool _dirty = false;
@@ -180,6 +183,11 @@ public class ColumnTypeInfoRegistry
return _map.GetValueOrDefault(type);
}
+ public IColumnTypeInfo GetRequiredByDataType(Type type)
+ {
+ return GetByDataType(type) ?? throw new Exception("Unsupported type.");
+ }
+
public string GetSqlType(Type type)
{
EnsureValidity();
@@ -190,7 +198,7 @@ public class ColumnTypeInfoRegistry
throw new Exception("Unsupported type for sql.");
}
- while (!typeof(IBuiltinColumnTypeInfo).IsInstanceOfType(current))
+ while (current is not IBuiltinColumnTypeInfo)
{
current = GetByDataType(current.GetDatabaseType());
Debug.Assert(current is not null);
diff --git a/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/TableInfo.cs b/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/TableInfo.cs
new file mode 100644
index 0000000..fb9e1ad
--- /dev/null
+++ b/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/TableInfo.cs
@@ -0,0 +1,73 @@
+namespace CrupestApi.Commons.Crud;
+
+public class TableInfo
+{
+ public TableInfo(Type entityType)
+ {
+ EntityType = entityType;
+
+ var properties = entityType.GetProperties();
+
+ var columnInfos = new List<ColumnInfo>();
+
+ bool hasPrimaryKey = false;
+ bool hasId = false;
+
+ foreach (var property in properties)
+ {
+ var columnInfo = new ColumnInfo(entityType, property.Name);
+ columnInfos.Add(columnInfo);
+ if (columnInfo.IsPrimaryKey)
+ hasPrimaryKey = true;
+ if (columnInfo.SqlColumnName.Equals("id", StringComparison.OrdinalIgnoreCase))
+ {
+ hasId = true;
+ }
+ }
+
+ 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)));
+ columnInfos.Add(columnInfo);
+ }
+
+ ColumnInfos = columnInfos;
+
+ CheckValidity();
+ }
+
+ public Type EntityType { get; }
+ public IReadOnlyList<ColumnInfo> ColumnInfos { get; }
+
+ public void CheckValidity()
+ {
+ // Check if there is only one primary key.
+ bool hasPrimaryKey = false;
+ foreach (var column in ColumnInfos)
+ {
+ if (column.IsPrimaryKey)
+ {
+ if (hasPrimaryKey) throw new Exception("Two columns are primary key.");
+ hasPrimaryKey = true;
+ }
+ }
+
+ if (!hasPrimaryKey) throw new Exception("No column is primary key.");
+
+ // Check two columns have the same sql name.
+ HashSet<string> sqlNameSet = new HashSet<string>();
+
+ foreach (var column in ColumnInfos)
+ {
+ if (sqlNameSet.Contains(column.SqlColumnName))
+ throw new Exception($"Two columns have the same sql name '{column.SqlColumnName}'.");
+ sqlNameSet.Add(column.SqlColumnName);
+ }
+ }
+
+ public string GenerateCreateTableSql()
+ {
+ throw new NotImplementedException();
+ }
+} \ No newline at end of file
diff --git a/docker/crupest-api/CrupestApi/CrupestApi.sln b/docker/crupest-api/CrupestApi/CrupestApi.sln
index 5f21ff5..ebfd960 100644
--- a/docker/crupest-api/CrupestApi/CrupestApi.sln
+++ b/docker/crupest-api/CrupestApi/CrupestApi.sln
@@ -11,6 +11,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CrupestApi.Secrets", "Crupe
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CrupestApi.Commons", "CrupestApi.Commons\CrupestApi.Commons.csproj", "{38083CCA-E56C-4D24-BAB6-EEC30E0F478F}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CrupestApi.Commons.Tests", "CrupestApi.Commons.Tests\CrupestApi.Commons.Tests.csproj", "{0D0304BF-6A18-444C-BAF4-6ABFF98A0F77}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -36,5 +38,9 @@ Global
{38083CCA-E56C-4D24-BAB6-EEC30E0F478F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{38083CCA-E56C-4D24-BAB6-EEC30E0F478F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{38083CCA-E56C-4D24-BAB6-EEC30E0F478F}.Release|Any CPU.Build.0 = Release|Any CPU
+ {0D0304BF-6A18-444C-BAF4-6ABFF98A0F77}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {0D0304BF-6A18-444C-BAF4-6ABFF98A0F77}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {0D0304BF-6A18-444C-BAF4-6ABFF98A0F77}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {0D0304BF-6A18-444C-BAF4-6ABFF98A0F77}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobal