aboutsummaryrefslogtreecommitdiff
path: root/docker
diff options
context:
space:
mode:
authorcrupest <crupest@outlook.com>2022-12-13 11:14:30 +0800
committercrupest <crupest@outlook.com>2022-12-20 20:32:53 +0800
commite42b048abf6c97515686c42175c29191b1527dfd (patch)
tree9d2c97e517a36644ac5069469249475a17310433 /docker
parentbc2a3db855cca16d4f66b23f814528671b5d8591 (diff)
downloadcrupest-e42b048abf6c97515686c42175c29191b1527dfd.tar.gz
crupest-e42b048abf6c97515686c42175c29191b1527dfd.tar.bz2
crupest-e42b048abf6c97515686c42175c29191b1527dfd.zip
Develop secret api. v33
Diffstat (limited to 'docker')
-rw-r--r--docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/ColumnInfo.cs71
-rw-r--r--docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/ColumnMetadata.cs33
-rw-r--r--docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/EntityJsonHelper.cs38
-rw-r--r--docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/README.md27
-rw-r--r--docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/TableInfo.cs72
5 files changed, 159 insertions, 82 deletions
diff --git a/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/ColumnInfo.cs b/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/ColumnInfo.cs
index 6e29de0..ae5081d 100644
--- a/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/ColumnInfo.cs
+++ b/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/ColumnInfo.cs
@@ -21,6 +21,7 @@ public class ColumnHooks
/// <summary>Called after SELECT. Please use multicast if you want to customize it because there are many default behavior in it.</summary>
/// <remarks>
+ /// Called after column type transformation.
/// value(in):
/// null => not found in SELECT result
/// DbNullValue => database NULL
@@ -33,6 +34,7 @@ public class ColumnHooks
/// <summary>Called before INSERT. Please use multicast if you want to customize it because there are many default behavior in it.</summary>
/// <remarks>
+ /// Called before column type transformation.
/// value(in):
/// null => not specified by insert clause
/// DbNullValue => specified as database NULL
@@ -45,6 +47,7 @@ public class ColumnHooks
/// <summary>Called before UPDATE. Please use multicast if you want to customize it because there are many default behavior in it.</summary
/// <remarks>
+ /// Called before column type transformation.
/// value(in):
/// null => not specified by update clause
/// DbNullValue => specified as database NULL
@@ -115,6 +118,7 @@ public class ColumnInfo
public bool IsNotNull => IsPrimaryKey || Metadata.GetValueOrDefault(ColumnMetadataKeys.NotNull) is true;
public bool IsGenerated => Metadata.GetValueOrDefault(ColumnMetadataKeys.Generated) is true;
public bool IsNoUpdate => Metadata.GetValueOrDefault(ColumnMetadataKeys.NoUpdate) is true;
+ public bool CanBeGenerated => DefaultValueGeneratorMethod is not null;
/// <summary>
/// This only returns metadata value. It doesn't not fall back to primary column. If you want to get the real key column, go to table info.
/// </summary>
@@ -122,7 +126,6 @@ public class ColumnInfo
/// <seealso cref="TableInfo.KeyColumn"/>
public bool IsSpecifiedAsKey => Metadata.GetValueOrDefault(ColumnMetadataKeys.ActAsKey) is true;
public ColumnIndexType Index => Metadata.GetValueOrDefault<ColumnIndexType?>(ColumnMetadataKeys.Index) ?? ColumnIndexType.None;
- public UpdateBehavior UpdateBehavior => Metadata.GetValueOrDefault<UpdateBehavior?>(ColumnMetadataKeys.UpdateBehavior) ?? UpdateBehavior.NullIsNotUpdate;
/// <summary>
/// The real column name. Maybe set in metadata or just the property name.
@@ -160,53 +163,71 @@ public class ColumnInfo
}
}
- private void TryCoerceStringFromNullToEmpty(ref object? value)
+ public MethodInfo ValidatorMethod
{
- if (ColumnType.ClrType == typeof(string) && (Metadata.GetValueOrDefault<bool?>(ColumnMetadataKeys.DefaultEmptyForString) is true) && value is DbNullValue)
+ get
{
- value = "";
+ object? value = Metadata.GetValueOrDefault(ColumnMetadataKeys.Validator);
+ Debug.Assert(value is null || value is string);
+ if (value is null)
+ {
+ return GetType().GetMethod(nameof(DefaultValidator))!;
+ }
+ else
+ {
+ string methodName = (string)value;
+ return Table.EntityType.GetMethod(methodName, BindingFlags.Static) ?? throw new Exception("The validator does not exist.");
+ }
}
}
- protected void OnAfterSelect(ColumnInfo column, ref object? value)
+ public void InvokeValidator(object value)
{
- TryCoerceStringFromNullToEmpty(ref value);
+ ValidatorMethod.Invoke(null, new object?[] { this, value });
}
- protected void OnBeforeInsert(ColumnInfo column, ref object? value)
+ public static void DefaultValidator(ColumnInfo column, object value)
{
- if (column.IsGenerated && value is not null)
+ if (column.IsNotNull && value is DbNullValue)
{
- throw new Exception($"'{column.ColumnName}' can't be set manually. It is auto generated.");
+ throw new Exception("The column can't be null.");
}
+ }
+
+ public object? InvokeDefaultValueGenerator()
+ {
+ return DefaultValueGeneratorMethod?.Invoke(null, new object?[] { this });
+ }
+
+ public static object? DefaultDefaultValueGenerator(ColumnInfo column)
+ {
+ return DbNullValue.Instance;
+ }
- var defaultValueGenerator = DefaultValueGeneratorMethod;
- if (defaultValueGenerator is not null && value is null)
+ private void TryCoerceStringFromNullToEmpty(ref object? value)
+ {
+ if (ColumnType.ClrType == typeof(string) && (Metadata.GetValueOrDefault<bool?>(ColumnMetadataKeys.DefaultEmptyForString) is true) && value is DbNullValue)
{
- value = defaultValueGenerator.Invoke(null, null);
+ value = "";
}
+ }
+ protected void OnAfterSelect(ColumnInfo column, ref object? value)
+ {
TryCoerceStringFromNullToEmpty(ref value);
+ }
- if (IsNotNull && (value is null || value is DbNullValue))
- {
- throw new Exception($"'{column.ColumnName}' can't be null.");
- }
+ protected void OnBeforeInsert(ColumnInfo column, ref object? value)
+ {
+ TryCoerceStringFromNullToEmpty(ref value);
}
protected void OnBeforeUpdate(ColumnInfo column, ref object? value)
{
- if (column.IsNoUpdate && value is not null)
- {
- throw new Exception($"'{column.ColumnName}' is not updatable.");
- }
+ if (IsNoUpdate && value is not null)
+ throw new Exception("The column can't be updated.");
TryCoerceStringFromNullToEmpty(ref value);
-
- if (IsNotNull && value is DbNullValue)
- {
- throw new Exception($"'{column.ColumnName}' can't be null.");
- }
}
public string GenerateCreateTableColumnString(string? dbProviderId = null)
diff --git a/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/ColumnMetadata.cs b/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/ColumnMetadata.cs
index c02f776..1082ea4 100644
--- a/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/ColumnMetadata.cs
+++ b/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/ColumnMetadata.cs
@@ -20,11 +20,18 @@ public static class ColumnMetadataKeys
/// <summary>
/// The default value generator method name in entity type. Default to null, aka, search for ColumnNameDefaultValueGenerator.
+ /// Generator has signature <code>static void DefaultValueGenerator(ColumnInfo column)</code>
/// </summary>
- /// <returns></returns>
public const string DefaultValueGenerator = nameof(ColumnAttribute.DefaultValueGenerator);
/// <summary>
+ /// The validator method name in entity type. Default to null, aka, the default validator.
+ /// Validator has signature <code>static void Validator(ColumnInfo column, object value)</code>
+ /// Value param is never null. If you want to mean NULL, it should be a <see cref="DbNullValue"/>.
+ /// </summary>
+ public const string Validator = nameof(ColumnAttribute.Validator);
+
+ /// <summary>
/// The column can only be set when inserted, can't be changed in update.
/// </summary>
/// <returns></returns>
@@ -34,24 +41,6 @@ public static class ColumnMetadataKeys
/// This column acts as key when get one entity for http get method in path.
/// </summary>
public const string ActAsKey = nameof(ColumnAttribute.ActAsKey);
-
- /// <summary>
- /// Define what to do when update.
- /// </summary>
- public const string UpdateBehavior = nameof(ColumnAttribute.UpdateBehavior);
-}
-
-[Flags]
-public enum UpdateBehavior
-{
- /// <summary>
- /// Null value means do not update that column.
- /// </summary>
- NullIsNotUpdate = 0,
- /// <summary>
- /// Null value means set to null.
- /// </summary>
- NullIsSetNull = 1
}
public interface IColumnMetadata
@@ -125,15 +114,15 @@ public class ColumnAttribute : Attribute, IColumnMetadata
/// <seealso cref="ColumnMetadataKeys.DefaultValueGenerator"/>
public string? DefaultValueGenerator { get; init; }
+ /// <seealso cref="ColumnMetadataKeys.Validator"/>
+ public string? Validator { get; init; }
+
/// <seealso cref="ColumnMetadataKeys.NoUpdate"/>
public bool NoUpdate { get; init; }
/// <seealso cref="ColumnMetadataKeys.ActAsKey"/>
public bool ActAsKey { get; init; }
- /// <seealso cref="ColumnMetadataKeys.UpdateBehavior">
- public UpdateBehavior UpdateBehavior { get; init; } = UpdateBehavior.NullIsNotUpdate;
-
public bool TryGetValue(string key, out object? value)
{
var property = GetType().GetProperty(key);
diff --git a/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/EntityJsonHelper.cs b/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/EntityJsonHelper.cs
index 19208d8..bbbbb4c 100644
--- a/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/EntityJsonHelper.cs
+++ b/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/EntityJsonHelper.cs
@@ -53,4 +53,42 @@ public class EntityJsonHelper<TEntity> where TEntity : class
var dictionary = ConvertEntityToDictionary(entity);
return JsonSerializer.Serialize(dictionary, _jsonSerializerOptions);
}
+
+ public virtual TEntity ConvertDictionaryToEntityForInsert(IReadOnlyDictionary<string, object?> dictionary)
+ {
+ var result = Activator.CreateInstance<TEntity>()!;
+
+ foreach (var column in _table.PropertyColumns)
+ {
+ var propertyInfo = column.PropertyInfo!;
+ var value = dictionary.GetValueOrDefault(column.ColumnName);
+ if (column.IsGenerated)
+ {
+ if (value is not null)
+ {
+ throw new UserException($"{propertyInfo.Name} is auto generated. Don't specify it.");
+ }
+ }
+
+ if (value is null)
+ {
+ if (column.IsNotNull && !column.CanBeGenerated)
+ {
+ throw new UserException($"{propertyInfo.Name} can't be null.");
+ }
+ propertyInfo.SetValue(result, null);
+ }
+ else
+ {
+ // Check type
+ var columnType = column.ColumnType;
+ if (columnType.ClrType.IsAssignableFrom(value.GetType()))
+ propertyInfo.SetValue(result, value);
+ else
+ throw new UserException($"{propertyInfo.Name} is of wrong type.");
+ }
+ }
+
+ return result;
+ }
}
diff --git a/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/README.md b/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/README.md
index 589b0a8..22289cb 100644
--- a/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/README.md
+++ b/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/README.md
@@ -10,20 +10,33 @@ The ultimate CRUD scaffold finally comes.
1. Create select `what`, where clause, order clause, `Offset` and `Limit`.
2. Check clauses' related columns are valid. Then generate sql string and param list.
-3. Convert param list to `Dapper` dynamic params. Execute sql and get `dynamic`s.
-4. Run hook `AfterSelect` for every column.
+3. Convert param list to `Dapper` dynamic params with proper type conversion in `IColumnTypeInfo`. Execute sql and get `dynamic`s.
+4. For each column:
+ 1. If column not in query result is null, null will be used to call hooks.
+ 2. If column is `NULL`, `DbNullValue` will be used to call hooks.
+ 3. Otherwise run conversion in `IColumnTypeInfo`.
+ 4. Run hook `AfterSelect` for every column.
5. Convert `dynamic`s to `TEntity`s.
### Insert
1. Create insert clause consisting of insert items.
2. Check clauses' related columns are valid. Then generate sql string and param list.
-3. Run hook `BeforeInsert` for every column.
-4. Convert param list to `Dapper` dynamic params. Execute sql and return `KeyColumn` value.
+3. For each column:
+ 1. If insert item exits and value is not null but the column `IsGenerated` is true, throw exception.
+ 2. If insert item does not exist or value is `null` for that column, use default value generator to generate value. However, `DbNullValue` always means use `NULL` for that column.
+ 3. Coerce null to `DbNullValue`.
+ 4. Run hook `BeforeInsert`.
+ 5. Coerce null to `DbNullValue`.
+ 6. Run validator.
+4. Convert param list to `Dapper` dynamic params with proper type conversion in `IColumnTypeInfo`. Execute sql and return `KeyColumn` value.
### Update
-1. Create update clause consisting of update items, where clause.
+1. Create update clause consisted of update items, where clause.
2. Check clauses' related columns are valid. Then generate sql string and param list.
-3. Run hook `BeforeUpdate` for every column.
-4. Convert param list to `Dapper` dynamic params. Execute sql and get count of affected rows.
+3. For each column:
+ 1. If insert item does not exist, `null` will be used to call hooks. However, `DbNullValue` always means use `NULL` for that column.
+ 2. Run hook `BeforeInsert`. If value is null, it means do not update this column.
+ 3. Run validator if `value` is not null.
+4. Convert param list to `Dapper` dynamic params with proper type conversion in `IColumnTypeInfo`. Execute sql and get count of affected rows.
diff --git a/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/TableInfo.cs b/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/TableInfo.cs
index b511b68..15b6320 100644
--- a/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/TableInfo.cs
+++ b/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/TableInfo.cs
@@ -431,7 +431,7 @@ CREATE TABLE {tableName}(
else
{
value = dynamicProperty.GetValue(d);
- if (value is null || value is DbNullValue)
+ if (value is null)
value = DbNullValue.Instance;
else
value = column.ColumnType.ConvertFromDatabase(value);
@@ -502,25 +502,44 @@ CREATE TABLE {tableName}(
foreach (var column in Columns)
{
InsertItem? item = insert.Items.SingleOrDefault(i => i.ColumnName == column.ColumnName);
- if (item is null)
+ object? value;
+ if (item is null || item.Value is null)
{
- object? value = null;
- column.Hooks.BeforeInsert(column, ref value);
- if (value is null || value is DbNullValue)
- realInsert.Add(column.ColumnName, null);
- else
- realInsert.Add(column.ColumnName, value);
+ value = null;
}
else
{
- object? value = item.Value ?? DbNullValue.Instance;
- column.Hooks.BeforeInsert(column, ref value);
- if (value is null || value is DbNullValue)
- realInsert.Add(column.ColumnName, null);
- else
- realInsert.Add(column.ColumnName, value);
+ value = item.Value;
+ }
+
+ if (column.IsGenerated && value is not null)
+ {
+ throw new Exception("The column is generated. You can't specify it explicitly.");
+ }
+
+ if (value is null)
+ {
+ value = column.InvokeDefaultValueGenerator();
+ }
+
+ if (value is null)
+ {
+ value = DbNullValue.Instance;
}
+ column.Hooks.BeforeInsert(column, ref value);
+
+ if (value is null)
+ value = DbNullValue.Instance;
+
+ column.InvokeValidator(value);
+
+ if (value is DbNullValue)
+ realInsert.Add(column.ColumnName, null);
+ else
+ realInsert.Add(column.ColumnName, value);
+
+
if (item?.ColumnName == KeyColumn.ColumnName)
{
key = item.Value;
@@ -546,25 +565,22 @@ CREATE TABLE {tableName}(
{
UpdateItem? item = update.Items.FirstOrDefault(i => i.ColumnName == column.ColumnName);
+ object? value;
if (item is null)
{
- object? value = null;
- column.Hooks.BeforeUpdate(column, ref value);
- if (value is not null)
- if (value is DbNullValue)
- realUpdate.Add(column.ColumnName, null);
- else
- realUpdate.Add(column.ColumnName, value);
+ value = null;
}
else
{
- object? value = item.Value ?? DbNullValue.Instance;
- column.Hooks.BeforeUpdate(column, ref value);
- if (value is not null)
- if (value is DbNullValue)
- realUpdate.Add(column.ColumnName, null);
- else
- realUpdate.Add(column.ColumnName, value);
+ value = item.Value ?? DbNullValue.Instance;
+ }
+
+ column.Hooks.BeforeUpdate(column, ref value);
+
+ if (value is not null)
+ {
+ column.InvokeValidator(value);
+ realUpdate.Add(column.ColumnName, value);
}
}