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.
		
		
		
		
		
			
		
			
				
					
					
						
							263 lines
						
					
					
						
							11 KiB
						
					
					
				
			
		
		
		
			
			
			
		
		
	
	
							263 lines
						
					
					
						
							11 KiB
						
					
					
				
								using System;
							 | 
						|
								using System.Collections.Generic;
							 | 
						|
								using System.Reflection;
							 | 
						|
								
							 | 
						|
								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 List<IndexAttribute> _indexes = new List<IndexAttribute>();
							 | 
						|
								
							 | 
						|
								        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; }
							 | 
						|
								
							 | 
						|
								        /// <summary>字段上的索引。</summary>
							 | 
						|
								        public IndexAttribute[] Indexes { get => _indexes.ToArray(); }
							 | 
						|
								
							 | 
						|
								        #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 indexes = IndexAttribute.Parse(property);
							 | 
						|
								            foreach (var index in indexes)
							 | 
						|
								            {
							 | 
						|
								                index.SetColumn(ca);
							 | 
						|
								            }
							 | 
						|
								            ca._indexes.AddRange(indexes);
							 | 
						|
								
							 | 
						|
								            // 检查免更新特性。
							 | 
						|
								            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;
							 | 
						|
								
							 | 
						|
								    }
							 | 
						|
								
							 | 
						|
								}
							 | 
						|
								
							 |