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
|
using System.Data;
using System.Reflection;
using System.Text;
namespace CrupestApi.Commons.Crud;
public delegate Task EntityPreSave(object? entity, ColumnInfo column, TableInfo table, IDbConnection connection);
public delegate Task EntityPostGet(object? entity, ColumnInfo column, TableInfo table, IDbConnection connection);
public class ColumnInfo
{
private Type ExtractRealTypeFromNullable(Type type)
{
if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>))
{
return type.GetGenericArguments()[0];
}
return type;
}
// A column with no property.
public ColumnInfo(Type entityType, string sqlColumnName, bool isPrimaryKey, bool isAutoIncrement, IColumnTypeInfo typeInfo, ColumnIndexType indexType = ColumnIndexType.None, ColumnTypeInfoRegistry? typeRegistry = null)
{
if (typeRegistry is null)
{
typeRegistry = ColumnTypeInfoRegistry.Singleton;
}
EntityType = entityType;
PropertyName = sqlColumnName;
PropertyType = typeof(int);
PropertyRealType = typeof(int);
SqlColumnName = sqlColumnName;
ColumnTypeInfo = typeInfo;
Nullable = false;
IsPrimaryKey = isPrimaryKey;
IsAutoIncrement = isAutoIncrement;
TypeRegistry = typeRegistry;
IndexType = indexType;
}
public ColumnInfo(Type entityType, string entityPropertyName, ColumnTypeInfoRegistry? typeRegistry = null)
{
if (typeRegistry is null)
{
typeRegistry = ColumnTypeInfoRegistry.Singleton;
}
EntityType = entityType;
PropertyName = entityPropertyName;
PropertyInfo = entityType.GetProperty(entityPropertyName);
if (PropertyInfo is null)
throw new Exception("Public property with given name does not exist.");
PropertyType = PropertyInfo.PropertyType;
PropertyRealType = ExtractRealTypeFromNullable(PropertyType);
var columnAttribute = PropertyInfo.GetCustomAttribute<ColumnAttribute>();
if (columnAttribute is null)
{
SqlColumnName = PropertyName;
Nullable = true;
IndexType = ColumnIndexType.None;
DefaultEmptyForString = false;
}
else
{
SqlColumnName = columnAttribute.DatabaseName ?? PropertyName;
Nullable = !columnAttribute.NonNullable;
IndexType = columnAttribute.IndexType;
DefaultEmptyForString = columnAttribute.DefaultEmptyForString;
}
ColumnTypeInfo = typeRegistry.GetRequiredByDataType(PropertyRealType);
TypeRegistry = typeRegistry;
if (DefaultEmptyForString)
{
EntityPostGet += (entity, column, _, _) =>
{
var pi = column.PropertyInfo;
if (pi is not null && column.ColumnTypeInfo.GetDatabaseType() == typeof(string))
{
var value = pi.GetValue(entity);
if (value is null)
{
pi.SetValue(entity, string.Empty);
}
}
return Task.CompletedTask;
};
}
}
public Type EntityType { get; }
// If null, there is no corresponding property.
public PropertyInfo? PropertyInfo { get; } = null;
public string PropertyName { get; }
public Type PropertyType { get; }
public Type PropertyRealType { get; }
public string SqlColumnName { get; }
public ColumnTypeInfoRegistry TypeRegistry { get; set; }
public IColumnTypeInfo ColumnTypeInfo { get; }
public bool Nullable { get; }
public bool IsPrimaryKey { get; }
public bool IsAutoIncrement { get; }
public ColumnIndexType IndexType { get; }
public bool DefaultEmptyForString { get; }
public event EntityPreSave? EntityPreSave;
public event EntityPostGet? EntityPostGet;
public string SqlType => TypeRegistry.GetSqlType(ColumnTypeInfo);
public string GenerateCreateTableColumnString()
{
StringBuilder result = new StringBuilder();
result.Append(SqlColumnName);
result.Append(' ');
result.Append(SqlType);
if (IsPrimaryKey)
{
result.Append(' ');
result.Append("PRIMARY KEY");
}
else if (!Nullable)
{
result.Append(' ');
result.Append(" NOT NULL");
}
if (IsAutoIncrement)
{
result.Append(' ');
result.Append("AUTOINCREMENT");
}
return result.ToString();
}
}
|