diff options
Diffstat (limited to 'docker/crupest-api/CrupestApi/CrupestApi.Commons')
6 files changed, 131 insertions, 41 deletions
diff --git a/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/ColumnInfo.cs b/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/ColumnInfo.cs index 88b5ced..f1fb99b 100644 --- a/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/ColumnInfo.cs +++ b/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/ColumnInfo.cs @@ -86,16 +86,39 @@ public class ColumnInfo protected void OnBeforeInsert(ColumnInfo column, ref object? value) { - TryCoerceStringFromNullToEmpty(ref value); - if (column.IsNotNull && !column.IsAutoIncrement) + if (column.IsClientGenerate && value is not null) + { + throw new Exception($"Column {column.ColumnName} can't be set manually."); + } + + var defaultValueGeneratorMethod = DefaultValueGeneratorMethod; + if (defaultValueGeneratorMethod is not null) { - throw new Exception($"Column {column.ColumnName} can't be empty."); + value = defaultValueGeneratorMethod.Invoke(null, new object[] { }); } + + + OnBeforeSet(column, ref value); } protected void OnBeforeUpdate(ColumnInfo column, ref object? value) { + OnBeforeSet(column, ref value); + + if (column.IsNoUpdate) + { + throw new Exception($"Column {column.ColumnName} not updatable."); + } + } + + protected void OnBeforeSet(ColumnInfo column, ref object? value) + { TryCoerceStringFromNullToEmpty(ref value); + + if (value is null && column.IsNotNull) + { + throw new Exception($"Column {column.ColumnName} can't be null."); + } } public string ColumnName @@ -108,9 +131,33 @@ public class ColumnInfo } } + public MethodInfo? DefaultValueGeneratorMethod + { + get + { + object? value = Metadata.GetValueOrDefault(ColumnMetadataKeys.DefaultValueGenerator); + Debug.Assert(value is null || value is string); + MethodInfo? result; + if (value is null) + { + string methodName = ColumnName + "DefaultValueGenerator"; + result = Table.EntityType.GetMethod(methodName, BindingFlags.Static); + } + else + { + string methodName = (string)value; + result = Table.EntityType.GetMethod(methodName, BindingFlags.Static) ?? throw new Exception("The default value generator does not exist."); + } + + return result; + } + } + public bool IsPrimaryKey => Metadata.GetValueOrDefault(ColumnMetadataKeys.IsPrimaryKey) is true; public bool IsAutoIncrement => Metadata.GetValueOrDefault(ColumnMetadataKeys.IsAutoIncrement) is true; public bool IsNotNull => IsPrimaryKey || Metadata.GetValueOrDefault(ColumnMetadataKeys.NotNull) is true; + public bool IsClientGenerate => Metadata.GetValueOrDefault(ColumnMetadataKeys.ClientGenerate) is true; + public bool IsNoUpdate => Metadata.GetValueOrDefault(ColumnMetadataKeys.NoUpdate) is true; public ColumnIndexType Index => Metadata.GetValueOrDefault<ColumnIndexType?>(ColumnMetadataKeys.Index) ?? ColumnIndexType.None; diff --git a/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/ColumnMetadata.cs b/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/ColumnMetadata.cs index 91e49f8..1ca2ce8 100644 --- a/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/ColumnMetadata.cs +++ b/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/ColumnMetadata.cs @@ -17,6 +17,18 @@ public static class ColumnMetadataKeys /// This indicates that you take care of generate this column value when create entity. User calling the api can not specify the value. /// </summary> public const string ClientGenerate = nameof(ColumnAttribute.DefaultEmptyForString); + + /// <summary> + /// The default value generator method name in entity type. Default to null, aka, search for ColumnNameDefaultValueGenerator. + /// </summary> + /// <returns></returns> + public const string DefaultValueGenerator = nameof(ColumnAttribute.DefaultValueGenerator); + + /// <summary> + /// The column can only be set when inserted, can't be changed in update. + /// </summary> + /// <returns></returns> + public const string NoUpdate = nameof(ColumnAttribute.NoUpdate); } public interface IColumnMetadata @@ -87,6 +99,12 @@ public class ColumnAttribute : Attribute, IColumnMetadata /// <see cref="ColumnMetadataKeys.ClientGenerate"/> public bool ClientGenerate { get; init; } + /// <see cref="ColumnMetadataKeys.DefaultValueGenerator"/> + public string? DefaultValueGenerator { get; init; } + + /// <see cref="ColumnMetadataKeys.NoUpdate"/> + public bool NoUpdate { get; init; } + public bool TryGetValue(string key, out object? value) { var property = GetType().GetProperty(key); diff --git a/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/CrudService.cs b/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/CrudService.cs index e098aca..811b2e6 100644 --- a/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/CrudService.cs +++ b/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/CrudService.cs @@ -40,6 +40,16 @@ public class CrudService<TEntity> : IDisposable where TEntity : class return _table.Select(_dbConnection, filter).Cast<TEntity>().ToList(); } + public bool Exists(IWhereClause? filter) + { + return _table.SelectCount(_dbConnection, filter) > 0; + } + + public int Count(IWhereClause? filter) + { + return _table.SelectCount(_dbConnection, filter); + } + public int Insert(IInsertClause insertClause) { return _table.Insert(_dbConnection, insertClause); diff --git a/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/CrudWebApplicationExtensions.cs b/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/CrudWebApplicationExtensions.cs index aa25a23..3467625 100644 --- a/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/CrudWebApplicationExtensions.cs +++ b/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/CrudWebApplicationExtensions.cs @@ -2,8 +2,13 @@ namespace CrupestApi.Commons.Crud; public static class CrudWebApplicationExtensions { - public static IApplicationBuilder UseCrud(this IApplicationBuilder app, string path) + public static WebApplication UseCrud(this WebApplication app, string path) { + app.MapGet(path, async (context) => + { + + }); + return app; } } diff --git a/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/DbConnectionFactory.cs b/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/DbConnectionFactory.cs index 2ee01ca..e8f8abf 100644 --- a/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/DbConnectionFactory.cs +++ b/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/DbConnectionFactory.cs @@ -4,7 +4,6 @@ using Microsoft.Extensions.Options; namespace CrupestApi.Commons.Crud; -// TODO: Implement and register this service. public interface IDbConnectionFactory { IDbConnection Get(string? name = null); diff --git a/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/TableInfo.cs b/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/TableInfo.cs index 28dc1ad..5bb19ad 100644 --- a/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/TableInfo.cs +++ b/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/TableInfo.cs @@ -207,7 +207,7 @@ CREATE TABLE {tableName}( } } - public (string sql, DynamicParameters parameters) GenerateSelectSql(IWhereClause? whereClause, IOrderByClause? orderByClause = null, int? skip = null, int? limit = null, string? dbProviderId = null) + public (string sql, DynamicParameters parameters) GenerateSelectSql(string? what, IWhereClause? whereClause, IOrderByClause? orderByClause = null, int? skip = null, int? limit = null, string? dbProviderId = null) { CheckRelatedColumns(whereClause); CheckRelatedColumns(orderByClause); @@ -215,7 +215,7 @@ CREATE TABLE {tableName}( var parameters = new DynamicParameters(); StringBuilder result = new StringBuilder() - .Append("SELECT * FROM ") + .Append($"SELECT {what ?? "*"} FROM ") .Append(TableName); if (whereClause is not null) @@ -372,49 +372,43 @@ CREATE TABLE {tableName}( return result; } - private object? ConvertFromDynamicToEntity(dynamic d) + public virtual int SelectCount(IDbConnection dbConnection, IWhereClause? where = null, IOrderByClause? orderBy = null, int? skip = null, int? limit = null) { - if (d is null) return null; + var (sql, parameters) = GenerateSelectSql("COUNT(*)", where, orderBy, skip, limit); + return dbConnection.QuerySingle<int>(sql, parameters); - Type dynamicType = d.GetType(); - - var result = Activator.CreateInstance(EntityType); - - foreach (var column in ColumnInfos) - { - var propertyInfo = column.PropertyInfo; - if (propertyInfo is not null) - { - var dynamicProperty = dynamicType.GetProperty(column.ColumnName); - if (dynamicProperty is null) continue; - object? value = dynamicProperty.GetValue(d); - value = column.ColumnType.ConvertFromDatabase(value); - propertyInfo.SetValue(result, value); - } - } - - return result; } public virtual IEnumerable<object?> Select(IDbConnection dbConnection, IWhereClause? where = null, IOrderByClause? orderBy = null, int? skip = null, int? limit = null) { - var (sql, parameters) = GenerateSelectSql(where, orderBy, skip, limit); + return Select<IEnumerable<object?>>(dbConnection, null, where, orderBy, skip, limit); + } + + public virtual IEnumerable<TResult> Select<TResult>(IDbConnection dbConnection, string? what, IWhereClause? where = null, IOrderByClause? orderBy = null, int? skip = null, int? limit = null) + { + var (sql, parameters) = GenerateSelectSql(what, where, orderBy, skip, limit); return dbConnection.Query<dynamic>(sql, parameters).Select(d => { - var e = ConvertFromDynamicToEntity(d); + Type dynamicType = d.GetType(); + + var result = Activator.CreateInstance<TResult>(); foreach (var column in ColumnInfos) { + object? value = null; + var dynamicProperty = dynamicType.GetProperty(column.ColumnName); + if (dynamicProperty is not null) value = dynamicProperty.GetValue(d); + column.Hooks.AfterSelect(column, ref value); + if (value is not null) + value = column.ColumnType.ConvertFromDatabase(value); var propertyInfo = column.PropertyInfo; if (propertyInfo is not null) { - var value = propertyInfo.GetValue(e); - column.Hooks.AfterSelect(column, ref value); - propertyInfo.SetValue(e, value); + propertyInfo.SetValue(result, value); } } - return e; + return result; }); } @@ -422,12 +416,20 @@ CREATE TABLE {tableName}( { var (sql, parameters) = GenerateInsertSql(insert); - foreach (var item in insert.Items) + foreach (var column in ColumnInfos) { - var column = GetColumn(item.ColumnName); - var value = item.Value; + InsertItem? item = insert.Items.FirstOrDefault(i => i.ColumnName == column.ColumnName); + var value = item?.Value; column.Hooks.BeforeInsert(column, ref value); - item.Value = value; + if (item is null) + { + if (value is not null) + insert.Items.Add(new InsertItem(column.ColumnName, value)); + } + else + { + item.Value = value; + } } return dbConnection.Execute(sql, ConvertParameters(parameters)); @@ -437,13 +439,22 @@ CREATE TABLE {tableName}( { var (sql, parameters) = GenerateUpdateSql(where, update); - foreach (var item in update.Items) + foreach (var column in ColumnInfos) { - var column = GetColumn(item.ColumnName); - var value = item.Value; + UpdateItem? item = update.Items.FirstOrDefault(i => i.ColumnName == column.ColumnName); + var value = item?.Value; column.Hooks.BeforeUpdate(column, ref value); - item.Value = value; + if (item is null) + { + if (value is not null) + update.Items.Add(new UpdateItem(column.ColumnName, value)); + } + else + { + item.Value = value; + } } + return dbConnection.Execute(sql, ConvertParameters(parameters)); } |