You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

456 lines
17 KiB

using Apewer.Internals;
using Apewer;
using System;
using System.Collections.Generic;
using System.Data;
using System.Reflection;
using System.Text;
namespace Apewer.Source
{
/// <summary></summary>
[Serializable]
public sealed class TableStructure
{
private bool _locked = false;
private string _tablename = Constant.EmptyString;
private bool _independent = false;
private Dictionary<string, ColumnAttribute> _columns = new Dictionary<string, ColumnAttribute>();
internal TableStructure() { }
/// <summary>不依赖 Record 公共属性。</summary>
public bool Independent
{
get { return _independent; }
private set { _independent = value; }
}
/// <summary>表名称。</summary>
public string Table
{
get { return _tablename; }
private set { _tablename = value ?? ""; }
}
/// <summary>列信息。</summary>
public Dictionary<string, ColumnAttribute> Columns
{
get
{
if (_locked)
{
var copy = new Dictionary<string, ColumnAttribute>(_columns.Count);
foreach (var c in _columns) copy.Add(c.Key, c.Value);
return copy;
}
return _columns;
}
private set { _columns = value; }
}
/// <summary>锁定属性,阻止修改。</summary>
public void Lock()
{
_locked = true;
foreach (var c in _columns) c.Value.Lock();
}
#region cache
private static Dictionary<string, TableStructure> _tsc = new Dictionary<string, TableStructure>();
private static Dictionary<string, TableAttribute> _tac = new Dictionary<string, TableAttribute>();
#endregion
#region static
/// <summary></summary>
/// <exception cref="System.Exception"></exception>
/// <exception cref="System.ArgumentNullException"></exception>
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;
}
/// <summary></summary>
/// <exception cref="System.Exception"></exception>
/// <exception cref="System.ArgumentNullException"></exception>
public static TableStructure ParseModel<T>(bool useCache = true) where T : IRecord
{
return ParseModel(typeof(T), useCache);
}
/// <summary></summary>
/// <exception cref="System.Exception"></exception>
/// <exception cref="System.ArgumentNullException"></exception>
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<string, ColumnAttribute>();
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;
}
/// <summary></summary>
/// <param name="type"></param>
/// <exception cref="Exception"></exception>"
public static TableAttribute ParseTable<T>(bool useCache = true) where T : IRecord
{
return ParseTable(typeof(T), useCache);
}
/// <summary></summary>
/// <param name="type"></param>
/// <exception cref="Exception"></exception>"
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 = ClassUtility.ContainsAttribute<IndependentAttribute>(type, true);
// 锁定属性。
ta.Lock();
// 加入缓存。
if (useCache)
{
lock (_tac)
{
if (!_tac.ContainsKey(cacheKey))
{
_tac.Add(cacheKey, ta);
}
}
}
return ta;
}
/// <summary></summary>
/// <exception cref="Exception">Exception</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;
}
/// <summary>排序。</summary>
public static Dictionary<string, ColumnAttribute> SortColumns(Dictionary<string, ColumnAttribute> columns)
{
// if (type.BaseType.FullName.Equals(typeof(Record).FullName)) // 仅当使用基类时排序。
var sorted = new Dictionary<string, ColumnAttribute>();
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;
}
/// <summary>限定表名称/列名称。</summary>
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;
}
/// <summary>生成 IDataParameter 列表,用于 Insert 或 Update。</summary>
/// <exception cref="ArgumentNullException"></exception>
public List<IDataParameter> CreateDataParameters(Record entity, CreateDataParameterCallback callback, params string[] excluded)
{
if (entity == null) throw new ArgumentNullException("argEntity");
if (callback == null) throw new ArgumentNullException("argCallback");
entity.FixProperties();
var list = new List<IDataParameter>();
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
/// <summary>获取 Record 根类中的属性名称。</summary>
private static List<string> GetRootProperties()
{
var list = new List<string>();
var type = typeof(Record);
var properties = type.GetProperties();
foreach (var property in properties)
{
if (ClassUtility.ContainsAttribute<ColumnAttribute>(property, false))
{
list.Add(property.Name);
}
}
return list;
}
#endregion
}
}