using Apewer.Internals; using Apewer; using System; using System.Collections.Generic; using System.Data; using System.Reflection; using System.Text; namespace Apewer.Source { /// [Serializable] public sealed class TableStructure { private bool _locked = false; private string _tablename = Constant.EmptyString; private bool _independent = false; private Dictionary _columns = new Dictionary(); internal TableStructure() { } /// 不依赖 Record 公共属性。 public bool Independent { get { return _independent; } private set { _independent = value; } } /// 表名称。 public string Table { get { return _tablename; } private set { _tablename = value ?? ""; } } /// 列信息。 public Dictionary Columns { get { if (_locked) { var copy = new Dictionary(_columns.Count); foreach (var c in _columns) copy.Add(c.Key, c.Value); return copy; } return _columns; } private set { _columns = value; } } /// 锁定属性,阻止修改。 public void Lock() { _locked = true; foreach (var c in _columns) c.Value.Lock(); } #region cache private static Dictionary _tsc = new Dictionary(); private static Dictionary _tac = new Dictionary(); #endregion #region static /// /// /// public static TableStructure ParseModel(object entity, bool useCache = true) { if (entity == null) throw new ArgumentNullException("参数无效"); var type = entity.GetType(); var result = ParseModel(type, useCache); return result; } /// /// /// public static TableStructure ParseModel(bool useCache = true) where T : IRecord { return ParseModel(typeof(T), useCache); } /// /// /// public static TableStructure ParseModel(Type model, bool useCache = true) { var type = model; if (type == null) throw new ArgumentNullException("参数无效"); // 使用缓存。 var cacheKey = type.FullName; if (useCache) { var hint = null as TableStructure; lock (_tsc) { if (_tsc.ContainsKey(cacheKey)) { hint = _tsc[cacheKey]; } } if (hint != null) return hint; } // 检查基类。 // if (type.BaseType.FullName.Equals(typeof(DatabaseRecord).FullName) == false) return "基类不受支持。"; // 检查 Attribute。 var ta = ParseTable(type); // 获取所有属性。 var properties = type.GetProperties(); if (properties.LongLength < 1L) throw new Exception(TextGenerator.Merge("类 ", type.FullName, " 不包含属性。")); // Record 根类属性名。 var roots = GetRootProperties(); // 检查字段定义。键:属性名称。 var columns = new Dictionary(); foreach (var property in properties) { var ca = ParseColumn(type, property); if (ca == null) continue; // 检查冗余。 foreach (var column in columns) { if (column.Value.Field == ca.Field) { throw new Exception(TextGenerator.Merge("类 ", type.FullName, " 中,属性 ", property.Name, " 的列名称存在冗余。")); } } // 检查基类。 if (roots.Contains(ca.Property.Name)) ca.Independent = true; columns.Add(property.Name, ca); } // if (columns.Count < 1) throw new Exception(TextGenerator.Merge("类 ", type.FullName, " 不包含可用的列。")); // 排序。 columns = SortColumns(columns); // 返回结果。 var ts = new TableStructure(); ts.Table = ta.Name; ts.Independent = ta.Independent; ts.Columns = columns; // 锁定属性。 ts.Lock(); // 加入缓存。 if (useCache) { lock (_tsc) { if (!_tsc.ContainsKey(cacheKey)) { _tsc.Add(cacheKey, ts); } } } return ts; } /// /// /// " public static TableAttribute ParseTable(bool useCache = true) where T : IRecord { return ParseTable(typeof(T), useCache); } /// /// /// " public static TableAttribute ParseTable(Type type, bool useCache = true) { // 使用缓存。 var cacheKey = type.FullName; if (useCache) { var hint = null as TableAttribute; lock (_tac) { if (_tac.ContainsKey(cacheKey)) { hint = _tac[cacheKey]; } } if (hint != null) return hint; } var tas = type.GetCustomAttributes(typeof(TableAttribute), false); if (tas.LongLength < 1L) throw new Exception(TextGenerator.Merge("类 ", type.FullName, " 不包含 ", typeof(TableAttribute).FullName, "。")); if (tas.LongLength > 1L) throw new Exception(TextGenerator.Merge("类 ", type.FullName, " 包含多个 ", typeof(TableAttribute).FullName, "。")); var ta = (TableAttribute)tas[0]; if (TextVerifier.IsBlank(ta.Name)) { ta = new TableAttribute("_" + type.Name); if (TextVerifier.IsBlank(ta.Name)) throw new Exception(TextGenerator.Merge("类 ", type.FullName, " 的表名称无效。")); } ta.Independent = RuntimeUtility.ContainsAttribute(type, true); // 锁定属性。 ta.Lock(); // 加入缓存。 if (useCache) { lock (_tac) { if (!_tac.ContainsKey(cacheKey)) { _tac.Add(cacheKey, ta); } } } return ta; } /// /// Exception" public static ColumnAttribute ParseColumn(Type type, PropertyInfo property) { // 检查 Attributes。 var cas = property.GetCustomAttributes(typeof(ColumnAttribute), false); if (cas.LongLength < 1L) return null; if (cas.LongLength > 1L) throw new Exception(TextGenerator.Merge("类 ", type.FullName, " 中,属性 ", property.Name, " 包含多个 ", typeof(ColumnAttribute).FullName, "。")); var ca = (ColumnAttribute)cas[0]; // 检查列名称。 if (TextVerifier.IsBlank(ca.Field)) { ca = new ColumnAttribute("_" + property.Name, ca.Type, ca.Length, true); if (TextVerifier.IsBlank(ca.Field)) throw new Exception(TextGenerator.Merge("类 ", type.FullName, "中,属性 ", property.Name, " 的列名称无效。")); } // 检查属性方法。 var getter = property.GetGetMethod(false); var setter = property.GetSetMethod(false); if (getter == null) throw new Exception(TextGenerator.Merge("类 ", type.FullName, " 中,属性 ", property.Name, " 不支持获取。")); if (setter == null) throw new Exception(TextGenerator.Merge("类 ", type.FullName, " 中,属性 ", property.Name, " 不支持设置。")); // 类型兼容。 var pt = property.PropertyType; if (pt.Equals(typeof(byte[]).FullName)) ca.Type = ColumnType.Binary; else if (pt.Equals(typeof(Byte))) ca.Type = ColumnType.Integer; else if (pt.Equals(typeof(SByte))) ca.Type = ColumnType.Integer; else if (pt.Equals(typeof(Int16))) ca.Type = ColumnType.Integer; else if (pt.Equals(typeof(UInt16))) ca.Type = ColumnType.Integer; else if (pt.Equals(typeof(Int32))) ca.Type = ColumnType.Integer; else if (pt.Equals(typeof(UInt32))) ca.Type = ColumnType.Integer; else if (pt.Equals(typeof(Int64))) ca.Type = ColumnType.Integer; else if (pt.Equals(typeof(Single))) ca.Type = ColumnType.Float; else if (pt.Equals(typeof(Double))) ca.Type = ColumnType.Float; else if (pt.Equals(typeof(Decimal))) ca.Type = ColumnType.Float; else if (pt.Equals(typeof(DateTime))) ca.Type = ColumnType.DateTime; else if (pt.Equals(typeof(String))) { switch (ca.Type) { case ColumnType.Binary: case ColumnType.Integer: case ColumnType.Float: case ColumnType.DateTime: //throw new Exception(TextGenerator.Merge("类 ", type.FullName, " 中,属性 ", property.Name, " 的类型不受支持。")); ca.Type = ColumnType.NText; break; } } else { ca.Type = ColumnType.NText; } ca.Property = property; // 锁定属性。 ca.Lock(); return ca; } /// 排序。 public static Dictionary SortColumns(Dictionary columns) { // if (type.BaseType.FullName.Equals(typeof(Record).FullName)) // 仅当使用基类时排序。 var sorted = new Dictionary(); if (columns.ContainsKey("Created")) sorted.Add("Created", columns["Created"]); if (columns.ContainsKey("Updated")) sorted.Add("Updated", columns["Updated"]); if (columns.ContainsKey("Flag")) sorted.Add("Flag", columns["Flag"]); if (columns.ContainsKey("Remark")) sorted.Add("Remark", columns["Remark"]); if (columns.ContainsKey("Key")) sorted.Add("Key", columns["Key"]); foreach (var property in columns.Keys) { if (property == "Created") continue; if (property == "Updated") continue; if (property == "Flag") continue; if (property == "Remark") continue; if (property == "Key") continue; sorted.Add(property, columns[property]); } return sorted; } /// 限定表名称/列名称。 public static string RestrictName(string name, bool underline) { if (name == null || name == Constant.EmptyString) return Constant.EmptyString; var lower = name.ToLower(); var available = TextGenerator.Merge("_", Constant.NumberCollection, Constant.LowerCollection); var sb = new StringBuilder(); foreach (var c in lower) { if (available.IndexOf(c) >= 0) sb.Append(c); } lower = sb.ToString(); if (underline && !lower.StartsWith("_")) lower = TextGenerator.Merge("_", lower); while (lower.Length > 2 && lower.StartsWith("__")) lower = lower.Substring(1); if (lower == "_" || lower == Constant.EmptyString) return Constant.EmptyString; if (lower.Length > 255) lower = lower.Substring(0, 255); return lower; } private static IDataParameter GenerateDataParameter(Record entity, ColumnAttribute attribute, CreateDataParameterCallback callback) { var property = attribute.Property; if (property == null) return null; var getter = property.GetGetMethod(); if (getter == null) return null; var parameter = null as IDataParameter; var value = getter.Invoke(entity, null); // if (attribute.Type == ColumnType.Binary || attribute.Type == ColumnType.Integer || attribute.Type == ColumnType.Float) { var temp = value; if (property.PropertyType.FullName == typeof(Decimal).FullName) { temp = TextUtility.GetDouble(temp.ToString()); } parameter = callback(new Parameter(attribute.Field, temp, attribute.Type, attribute.Length)); } else if (attribute.Type == ColumnType.DateTime) { parameter = callback(new Parameter(attribute.Field, value, attribute.Type, 0)); } else if (property.PropertyType.Equals(typeof(String))) { var text = value as string; if (text == null) text = ""; if (attribute.Length > 0) { switch (attribute.Type) { case ColumnType.VarChar: case ColumnType.NVarChar: text = TextUtility.RestrictLength(text, attribute.Length); break; case ColumnType.VarChar255: case ColumnType.NVarChar255: text = TextUtility.RestrictLength(text, 255); break; } } parameter = callback(new Parameter(attribute.Field, text, attribute.Type, attribute.Length)); } else { var text = (value == null) ? TextUtility.EmptyString : value.ToString(); parameter = callback(new Parameter(attribute.Field, text, attribute.Type, attribute.Length)); } return parameter; } /// 生成 IDataParameter 列表,用于 Insert 或 Update。 /// public List CreateDataParameters(Record entity, CreateDataParameterCallback callback, params string[] excluded) { if (entity == null) throw new ArgumentNullException(nameof(entity)); if (callback == null) throw new ArgumentNullException(nameof(excluded)); entity.FixProperties(); var list = new List(); foreach (var column in Columns) { var attribute = column.Value; if (ParseTable(entity.GetType()).Independent && attribute.Independent) continue; var parameter = GenerateDataParameter(entity, attribute, callback); if (parameter == null) continue; var add = true; foreach (var exclude in excluded) { var lower = parameter.ParameterName.ToLower(); if (lower == exclude.ToLower()) { add = false; } } if (add) list.Add(parameter); } return list; } #endregion #region /// 获取 Record 根类中的属性名称。 private static List GetRootProperties() { var list = new List(); var type = typeof(Record); var properties = type.GetProperties(); foreach (var property in properties) { if (RuntimeUtility.ContainsAttribute(property, false)) { list.Add(property.Name); } } return list; } #endregion } }