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 { #region Instance Type _model = null; TableAttribute _table = null; ColumnAttribute _key = null; ColumnAttribute _flag = null; ColumnAttribute[] _columns = null; ColumnAttribute[] _fillable = null; private TableStructure() { } /// 使用此结构的记录模型。 public Type Model { get => _model; } /// 表特性。 public TableAttribute Table { get => _table; } /// 列信息。 public ColumnAttribute[] Columns { get => _columns; } /// 可填充的列信息。 public ColumnAttribute[] Fillable { get => _fillable; } /// 主键。 public ColumnAttribute Key { get => _key; } /// 记录标记。 public ColumnAttribute Flag { get => _flag; } /// 到 Boolean 的隐式转换,判断 有效。 /// True:存在有效结构时;
False:不存在有效结构。
public static implicit operator bool(TableStructure instance) { if (instance == null) return false; if (instance._model == null) return false; if (instance._columns == null) return false; return true; } #endregion #region Obsolete /// 不依赖 Record 公共属性。 /// 注意:此属性即将弃用,应改为使用 Table.Independent 属性。 public bool Independent { get => _table == null ? false : _table.Independent; } /// 表的说明信息。 /// 注意:此属性即将弃用,应改为使用 Table.Description 属性。 public string Description { get => _table == null ? null : _table.Description; } /// 表特性。 /// 注意:此属性即将弃用,应改为使用 Table 属性。 public TableAttribute Attribute { get => _table; } /// 使用模型的所有属性,对缺少 Column 特性的属性使用默认参数的 Column 特性。 /// 注意:此属性即将弃用,应改为使用 Table.AllProperties 属性。 public bool AllProperties { get => _table == null ? false : _table.AllProperties; } /// 表名。 /// 注意:此属性即将弃用,应改为使用 Table.Name 属性。 public string TableName { get => _table == null ? null : _table.Name; } #endregion #region TableStructure private static Dictionary _tsc = new Dictionary(); /// 解析表结构。 public static TableStructure Parse(bool useCache = true, bool force = false) where T : IRecord => Parse(typeof(T), useCache, force); /// 解析表结构。 /// 表结构。类型不可用于表结构时返回 NULL 值。 public static TableStructure Parse(Type model, bool useCache = true, bool force = false) { var type = model; if (type == null || !type.IsClass || type.IsAbstract) return null; // 使用缓存。 var cacheKey = type.FullName; if (force) cacheKey = "[force] " + cacheKey; if (useCache) { lock (_tsc) { TableStructure cached; if (_tsc.TryGetValue(cacheKey, out cached)) return cached; } } // 解析 TableAttribute。 var ta = TableAttribute.Parse(type, useCache, force); if (!ta && !force) return null; // 类型。 var isRecord = RuntimeUtility.IsInherits(type, typeof(Record)); var properties = type.GetProperties(); var total = properties.Length; // 解析 ColumnAttribute。 var key = null as ColumnAttribute; var flag = null as ColumnAttribute; var columns = new ColumnAttribute[total]; var columnsCount = 0; var fillable = new List(total); if (total > 0) { var caForce = force || (ta ? ta.AllProperties : false); var addedFields = new List(total); foreach (var property in properties) { var ca = ColumnAttribute.Parse(property, caForce); if (ca != null) { // 检查 field 重复,只保留第一个。 var field = ca.Field; if (!addedFields.Contains(field)) { addedFields.Add(field); columns[columnsCount] = ca; columnsCount += 1; if (isRecord) { if (property.Name == "Key") { key = ca; if (ta != null && ta.PrimaryKey) ca.SetPrimaryKey(); } else if (property.Name == "Flag") { flag = ca; } } } // 可查询的列。 fillable.Add(ca); continue; } // 可查询的列。 if (!caForce) { ca = ColumnAttribute.Parse(property, caForce); if (ca) fillable.Add(ca); } } } if (columnsCount > 0 && columnsCount != columns.Length) columns = columns.Slice(0, columnsCount); // 排序,将 Key 和 Flag 排在最前。 columns = ColumnAttribute.Sort(columns); // 返回结果。 var ts = new TableStructure(); ts._table = ta; ts._key = key; ts._flag = flag; ts._columns = columns; ts._model = model; ts._fillable = fillable.ToArray(); // 加入缓存。 if (useCache) { lock (_tsc) { if (!_tsc.ContainsKey(cacheKey)) _tsc.Add(cacheKey, ts); } } return ts; } #endregion #region TableAttribute // 限定表名称/列名称。 static string RestrictName(string name, bool underline = false, bool english = false) { if (string.IsNullOrEmpty(name)) return null; var str = name; // 限定名称仅使用英文和数字。 if (english) { const string available = "_0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; var chars = new ArrayBuilder(); var strChars = str.ToCharArray(); var strLength = strChars.Length > 255 ? 255 : strChars.Length; for (var i = 0; i < strLength; i++) { if (available.IndexOf(strChars[i]) > -1) chars.Add(strChars[i]); } str = new string(chars.Export()); } // 以下划线开始。 if (underline) { if (!str.StartsWith("_")) str = TextUtility.Merge("_", str); } return str; } static IDataParameter CreateParameter(object record, ColumnAttribute ca, Func callback) { var property = ca.Property; if (property == null) return null; var getter = property.GetGetMethod(); if (getter == null) return null; var value = getter.Invoke(record, null); if (ca.Type == ColumnType.Bytes || ca.Type == ColumnType.Integer || ca.Type == ColumnType.Float) { return callback(new Parameter(ca.Field, value, ca.Type, ca.Length)); } if (ca.Type == ColumnType.DateTime) { return callback(new Parameter(ca.Field, value, ca.Type, 0)); } if (property.PropertyType.Equals(typeof(String))) { var text = value as string; if (text == null) text = ""; if (ca.Length > 0) { switch (ca.Type) { case ColumnType.VarChar: case ColumnType.NVarChar: text = TextUtility.Left(text, ca.Length); break; case ColumnType.VarChar191: case ColumnType.NVarChar191: text = TextUtility.Left(text, 191); break; } } return callback(new Parameter(ca.Field, text, ca.Type, ca.Length)); } var defaultText = (value == null) ? TextUtility.Empty : value.ToString(); return callback(new Parameter(ca.Field, defaultText, ca.Type, ca.Length)); } /// 生成 IDataParameter 列表,用于 Insert 和 Update 方法。 public IDataParameter[] CreateParameters(object record, Func callback, IEnumerable excludeds) { if (record == null || callback == null) return null; var list = new List(_columns.Length); foreach (var ca in Columns) { if (ca == null) continue; var parameter = CreateParameter(record, ca, callback); if (parameter == null) continue; var add = true; if (excludeds != null) { var lower = parameter.ParameterName.ToLower(); foreach (var excluded in excludeds) { if (string.IsNullOrEmpty(excluded)) continue; if (lower == excluded.ToLower()) { add = false; break; } } } if (add) list.Add(parameter); } return list.ToArray(); } #endregion } }