aboutsummaryrefslogtreecommitdiff
path: root/docker/crupest-api/CrupestApi/CrupestApi.Commons/Crud/ColumnMetadata.cs
blob: c02f776d0b8e21dfcbfe7eb527eccfba7414f9ae (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
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);

    /// <summary>
    /// This will add hooks for string type column to coerce null to ""(empty string) when get or set. No effect on non-string type.
    /// </summary> 
    public const string DefaultEmptyForString = nameof(ColumnAttribute.DefaultEmptyForString);

    /// <summary>
    /// 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 Generated = nameof(ColumnAttribute.Generated);

    /// <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);

    /// <summary>
    /// 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
{
    bool TryGetValue(string key, out object? value);

    object? GetValueOrDefault(string key)
    {
        if (TryGetValue(key, out var value))
        {
            return value;
        }
        else
        {
            return null;
        }
    }

    T? GetValueOrDefault<T>(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;

    /// <seealso cref="ColumnMetadataKeys.DefaultEmptyForString"/>
    public bool DefaultEmptyForString { get; init; }

    /// <seealso cref="ColumnMetadataKeys.Generated"/>
    public bool Generated { get; init; }

    /// <seealso cref="ColumnMetadataKeys.DefaultValueGenerator"/>
    public string? DefaultValueGenerator { 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);
        if (property is null)
        {
            value = null;
            return false;
        }
        value = property.GetValue(this);
        return true;
    }
}

public class AggregateColumnMetadata : IColumnMetadata
{
    private IDictionary<string, object?> _own = new Dictionary<string, object?>();
    private IList<IColumnMetadata> _children = new List<IColumnMetadata>();

    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;
    }
}