namespace CrupestApi.Commons.Crud; public static class ColumnMetadataKeys { public const string ColumnName = nameof(ColumnAttribute.ColumnName); public const string NotNull = nameof(ColumnAttribute.NotNull); public const string IsPrimaryKey = nameof(ColumnAttribute.IsPrimaryKey); public const string IsAutoIncrement = nameof(ColumnAttribute.IsAutoIncrement); public const string Index = nameof(ColumnAttribute.Index); /// /// This will add hooks for string type column to coerce null to ""(empty string) when get or set. No effect on non-string type. /// public const string DefaultEmptyForString = nameof(ColumnAttribute.DefaultEmptyForString); /// /// This indicates that you take care of generate this column value when create entity. User calling the api can not specify the value. /// public const string ClientGenerate = nameof(ColumnAttribute.DefaultEmptyForString); /// /// The default value generator method name in entity type. Default to null, aka, search for ColumnNameDefaultValueGenerator. /// /// public const string DefaultValueGenerator = nameof(ColumnAttribute.DefaultValueGenerator); /// /// The column can only be set when inserted, can't be changed in update. /// /// public const string NoUpdate = nameof(ColumnAttribute.NoUpdate); /// /// This column acts as key when get one entity for http get method in path. /// public const string ActAsKey = nameof(ColumnAttribute.ActAsKey); /// /// Define what to do when update. /// public const string UpdateBehavior = nameof(ColumnAttribute.UpdateBehavior); } [Flags] public enum UpdateBehavior { /// /// Null value means do not update that column. /// NullIsNotUpdate = 0, /// /// Null value means set to null. /// NullIsSetNull = 1 } public interface IColumnMetadata { bool TryGetValue(string key, out object? value); object? GetValueOrDefault(string key) { if (TryGetValue(key, out var value)) { return value; } else { return null; } } T? GetValueOrDefault(string key) { return (T?)GetValueOrDefault(key); } object? this[string key] { get { if (TryGetValue(key, out var value)) { return value; } else { throw new KeyNotFoundException("Key not found."); } } } } public enum ColumnIndexType { None, Unique, NonUnique } [AttributeUsage(AttributeTargets.Property, AllowMultiple = false)] public class ColumnAttribute : Attribute, IColumnMetadata { // if null, use the property name. public string? ColumnName { get; init; } // default false. public bool NotNull { get; init; } // default false public bool IsPrimaryKey { get; init; } // default false public bool IsAutoIncrement { get; init; } // default None public ColumnIndexType Index { get; init; } = ColumnIndexType.None; /// public bool DefaultEmptyForString { get; init; } /// public bool ClientGenerate { get; init; } /// public string? DefaultValueGenerator { get; init; } /// public bool NoUpdate { get; init; } /// public bool ActAsKey { get; init; } /// public UpdateBehavior UpdateBehavior { get; init; } = UpdateBehavior.NullIsNotUpdate; public bool TryGetValue(string key, out object? value) { var property = GetType().GetProperty(key); if (property is null) { value = null; return false; } value = property.GetValue(this); return true; } } public class AggregateColumnMetadata : IColumnMetadata { private IDictionary _own = new Dictionary(); private IList _children = new List(); public void Add(string key, object? value) { _own[key] = value; } public void Remove(string key) { _own.Remove(key); } public void Add(IColumnMetadata child) { _children.Add(child); } public void Remove(IColumnMetadata child) { _children.Remove(child); } public bool TryGetValue(string key, out object? value) { if (_own.ContainsKey(key)) { value = _own[key]; return true; } bool found = false; value = null; foreach (var child in _children) { if (child.TryGetValue(key, out var tempValue)) { value = tempValue; found = true; } } return found; } }