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 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 OnlyGenerated = nameof(ColumnAttribute.OnlyGenerated); /// /// The default value generator method name in entity type. Default to null, aka, search for ColumnNameDefaultValueGenerator. /// Generator has signature static void DefaultValueGenerator(ColumnInfo column) /// public const string DefaultValueGenerator = nameof(ColumnAttribute.DefaultValueGenerator); /// /// The validator method name in entity type. Default to null, aka, the default validator. /// Validator has signature static void Validator(ColumnInfo column, object value) /// Value param is never null. If you want to mean NULL, it should be a . /// public const string Validator = nameof(ColumnAttribute.Validator); /// /// 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); /// /// The default value used for the column. /// public const string DefaultValue = nameof(ColumnAttribute.DefaultValue); } 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 None public ColumnIndexType Index { get; init; } = ColumnIndexType.None; /// public bool DefaultEmptyForString { get; init; } /// public bool OnlyGenerated { get; init; } /// public string? DefaultValueGenerator { get; init; } /// public string? Validator { get; init; } /// public bool NoUpdate { get; init; } /// public bool ActAsKey { get; init; } public object? DefaultValue { get; init; } 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; } }