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.
252 lines
11 KiB
252 lines
11 KiB
using Apewer.Internals;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Reflection;
|
|
using System.Text;
|
|
|
|
namespace Apewer.Source
|
|
{
|
|
|
|
/// <summary>数据库中的列,类型默认为 NVarChar(191),错误类型将修正为默认类型。</summary>
|
|
/// <remarks>注意:当一个数据模型中存在多个相同的 Field 时,将只有第一个被保留。</remarks>
|
|
[Serializable]
|
|
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
|
|
public sealed class ColumnAttribute : Attribute, IToJson
|
|
{
|
|
|
|
private PropertyInfo _property = null;
|
|
internal string PropertyName = null;
|
|
|
|
private string _field = null;
|
|
private int _length = 0;
|
|
private ColumnType _type;
|
|
private bool _incremental = false;
|
|
private bool _primarykey = false;
|
|
private bool _noupdate = false;
|
|
|
|
private bool _independent = false;
|
|
private bool _valid = true;
|
|
private bool _nullable = false;
|
|
|
|
private void Init(string field, ColumnType type, int length)
|
|
{
|
|
_field = field;
|
|
_type = type;
|
|
switch (type)
|
|
{
|
|
case ColumnType.VarChar:
|
|
case ColumnType.NVarChar:
|
|
_length = length < 1 ? 191 : length;
|
|
break;
|
|
case ColumnType.VarChar191:
|
|
case ColumnType.NVarChar191:
|
|
_length = 191;
|
|
break;
|
|
default:
|
|
_length = length;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/// <summary>使用自动的列名称。当类型为 VarChar 或 NVarChar 时必须指定长度。</summary>
|
|
/// <exception cref="System.ArgumentOutOfRangeException"></exception>
|
|
public ColumnAttribute(ColumnType type = ColumnType.NVarChar191, int length = 191) => Init(null, type, length);
|
|
|
|
/// <summary>使用指定的列名称。当类型为 VarChar 或 NVarChar 时必须指定长度。</summary>
|
|
/// <exception cref="System.ArgumentOutOfRangeException"></exception>
|
|
public ColumnAttribute(string field, ColumnType type = ColumnType.NVarChar191, int length = 191) => Init(field, type, length);
|
|
|
|
/// <summary>字段名。</summary>
|
|
public string Field { get => _field; }
|
|
|
|
/// <summary>指定字段的最大长度。</summary>
|
|
public int Length { get => _length; }
|
|
|
|
/// <summary>字段类型。</summary>
|
|
public ColumnType Type { get => _type; }
|
|
|
|
#region 附加
|
|
|
|
/// <summary>此特性有效。</summary>
|
|
public bool Valid { get => _valid; }
|
|
|
|
/// <summary>此列不被管理,Insert、Update 和 Create Table 将忽略此列。</summary>
|
|
public bool Independent { get => _independent; }
|
|
|
|
/// <summary>使用此特性的属性。</summary>
|
|
public PropertyInfo Property { get => _property; }
|
|
|
|
/// <summary>属性对应的字段是主键。</summary>
|
|
public bool PrimaryKey { get => _primarykey; }
|
|
|
|
/// <summary>属性对应的字段是自动增长的整数。</summary>
|
|
public bool Incremental { get => _incremental; }
|
|
|
|
/// <summary>属性可为 NULL 值。</summary>
|
|
public bool Nullable { get => _nullable; }
|
|
|
|
/// <summary>在执行 ORM 的 Update 方法时,不包含此属性。</summary>
|
|
public bool NoUpdate { get => _noupdate; }
|
|
|
|
#endregion
|
|
|
|
/// <summary>生成 Json 对象。</summary>
|
|
public Json ToJson()
|
|
{
|
|
var json = Json.NewObject();
|
|
json.SetProperty("field", _field);
|
|
json.SetProperty("type", _type.ToString());
|
|
json.SetProperty("length", _length);
|
|
|
|
if (_property != null)
|
|
{
|
|
var property = Json.NewObject();
|
|
property.SetProperty("name", _property.Name);
|
|
property.SetProperty("type", _property.PropertyType.FullName);
|
|
|
|
json.SetProperty("property", property);
|
|
json.SetProperty("nullable", _nullable);
|
|
json.SetProperty("valid", _valid);
|
|
json.SetProperty("independent", _independent);
|
|
json.SetProperty("primarykey", _primarykey);
|
|
json.SetProperty("incremental", _incremental);
|
|
}
|
|
|
|
return json;
|
|
}
|
|
|
|
/// <summary>从 <see cref="ColumnAttribute"/> 到 Boolean 的隐式转换,判断 <see cref="ColumnAttribute"/> 有效。</summary>
|
|
public static implicit operator bool(ColumnAttribute instance) => instance != null;
|
|
|
|
/// <summary>解析列特性。</summary>
|
|
/// <remarks>注意:此方法不再抛出异常,当不存在正确的列特性时将返回 NULL 值</remarks>
|
|
public static ColumnAttribute Parse(PropertyInfo property, bool force = false)
|
|
{
|
|
if (property == null) return null;
|
|
|
|
// 属性带有 Independent 特性。
|
|
if (property.Contains<IndependentAttribute>()) return null;
|
|
|
|
// 检查 ColumnAttribute。
|
|
ColumnAttribute ca;
|
|
{
|
|
var cas = property.GetCustomAttributes(typeof(ColumnAttribute), false);
|
|
if (cas.LongLength < 1L)
|
|
{
|
|
if (!force) return null;
|
|
ca = new ColumnAttribute();
|
|
}
|
|
else ca = (ColumnAttribute)cas[0];
|
|
}
|
|
|
|
// 检查属性方法。
|
|
var getter = property.GetGetMethod(false);
|
|
var setter = property.GetSetMethod(false);
|
|
if (getter == null || getter.IsStatic) return null;
|
|
if (setter == null || setter.IsStatic) return null;
|
|
|
|
// 检查主键特性和自增特性。
|
|
var pt = property.PropertyType;
|
|
if (!ca._incremental && RuntimeUtility.Contains<IncrementalAttribute>(property))
|
|
{
|
|
if (pt.Equals(typeof(int)) || pt.Equals(typeof(long))) ca._incremental = true;
|
|
}
|
|
if (!ca._primarykey && RuntimeUtility.Contains<PrimaryKeyAttribute>(property)) ca._primarykey = true;
|
|
|
|
// 检查免更新特性。
|
|
var nu = RuntimeUtility.GetAttribute<NoUpdateAttribute>(property, false);
|
|
if (nu) ca._noupdate = true;
|
|
|
|
// 检查列名称。
|
|
if (TextUtility.IsBlank(ca.Field)) ca._field = property.Name;
|
|
|
|
// 类型兼容。
|
|
if (pt.Equals(typeof(byte[]))) ca._type = ColumnType.Bytes;
|
|
else if (pt.Equals(typeof(bool))) ca._type = ColumnType.Boolean;
|
|
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.Bytes:
|
|
case ColumnType.Integer:
|
|
case ColumnType.Float:
|
|
case ColumnType.DateTime:
|
|
//throw new Exception(TextGenerator.Merge("类 ", type.FullName, " 中,属性 ", property.Name, " 的类型不受支持。"));
|
|
ca._type = ColumnType.NVarChar;
|
|
ca._length = 191;
|
|
break;
|
|
}
|
|
}
|
|
#if !NET20
|
|
else if (pt.Equals(typeof(Nullable<Boolean>))) { ca._type = ColumnType.Boolean; ca._nullable = true; }
|
|
else if (pt.Equals(typeof(Nullable<Byte>))) { ca._type = ColumnType.Integer; ca._nullable = true; }
|
|
else if (pt.Equals(typeof(Nullable<SByte>))) { ca._type = ColumnType.Integer; ca._nullable = true; }
|
|
else if (pt.Equals(typeof(Nullable<Int16>))) { ca._type = ColumnType.Integer; ca._nullable = true; }
|
|
else if (pt.Equals(typeof(Nullable<UInt16>))) { ca._type = ColumnType.Integer; ca._nullable = true; }
|
|
else if (pt.Equals(typeof(Nullable<Int32>))) { ca._type = ColumnType.Integer; ca._nullable = true; }
|
|
else if (pt.Equals(typeof(Nullable<UInt32>))) { ca._type = ColumnType.Integer; ca._nullable = true; }
|
|
else if (pt.Equals(typeof(Nullable<Int64>))) { ca._type = ColumnType.Integer; ca._nullable = true; }
|
|
else if (pt.Equals(typeof(Nullable<Single>))) { ca._type = ColumnType.Float; ca._nullable = true; }
|
|
else if (pt.Equals(typeof(Nullable<Double>))) { ca._type = ColumnType.Float; ca._nullable = true; }
|
|
else if (pt.Equals(typeof(Nullable<Decimal>))) { ca._type = ColumnType.Float; ca._nullable = true; }
|
|
else if (pt.Equals(typeof(Nullable<DateTime>))) { ca._type = ColumnType.DateTime; ca._nullable = true; }
|
|
#endif
|
|
else
|
|
{
|
|
ca._type = ColumnType.NVarChar191;
|
|
ca._length = 191;
|
|
}
|
|
|
|
ca._property = property;
|
|
ca.PropertyName = property.Name;
|
|
|
|
return ca;
|
|
}
|
|
|
|
/// <summary>对列特性排序,Key 和 Flag 将始终排在前部。</summary>
|
|
public static ColumnAttribute[] Sort(ColumnAttribute[] columns, bool sort = false)
|
|
{
|
|
var total = columns.Length;
|
|
var key = null as ColumnAttribute;
|
|
var flag = null as ColumnAttribute;
|
|
var temp = new List<ColumnAttribute>(total);
|
|
for (var i = 0; i < total; i++)
|
|
{
|
|
var ca = columns[i];
|
|
if (ca == null) continue;
|
|
var pn = ca.Property.Name;
|
|
if (pn == "Key") key = ca;
|
|
else if (pn == "Flag") flag = ca;
|
|
else temp.Add(ca);
|
|
}
|
|
if (sort && temp.Count > 0) temp.Sort((a, b) => a._field.CompareTo(b._field));
|
|
|
|
if (key == null && flag == null) return temp.ToArray();
|
|
|
|
total = 0;
|
|
if (key != null) total += 1;
|
|
if (flag != null) total += 1;
|
|
total += temp.Count;
|
|
var sorted = new List<ColumnAttribute>(total);
|
|
if (key != null) sorted.Add(key);
|
|
if (flag != null) sorted.Add(flag);
|
|
sorted.AddRange(temp);
|
|
return sorted.ToArray();
|
|
}
|
|
|
|
internal void SetPrimaryKey() => _primarykey = true;
|
|
|
|
}
|
|
|
|
}
|