发布0101
大石头 authored at 2020-01-01 23:35:09
21.15 KiB
X
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Reflection;
using XCode.DataAccessLayer;

namespace XCode.Configuration
{
    /// <summary>数据属性元数据以及特性</summary>
    public class FieldItem
    {
        #region 属性
        /// <summary>属性元数据</summary>
        private readonly PropertyInfo _Property;

        /// <summary>绑定列特性</summary>
        private readonly BindColumnAttribute _Column;

        /// <summary>数据字段特性</summary>
        private readonly DataObjectFieldAttribute _DataObjectField;

        private readonly DescriptionAttribute _Description;

        private readonly DisplayNameAttribute _DisplayName;

        /// <summary>备注</summary>
        public String Description { get; internal set; }

        private String _dis;
        /// <summary>说明</summary>
        public String DisplayName
        {
            get
            {
                if (!_dis.IsNullOrEmpty()) return _dis;

                var name = Description;
                if (name.IsNullOrEmpty()) return Name;

                var p = name.IndexOf("。");
                if (p > 0) name = name.Substring(0, p);

                return name;
            }
            internal set { _dis = value; }
        }
        #endregion

        #region 扩展属性
        /// <summary>属性名</summary>
        public String Name { get; internal set; }

        /// <summary>属性类型</summary>
        public Type Type { get; internal set; }

        private Type _DeclaringType;
        /// <summary>声明类型</summary>
        internal Type DeclaringType
        {
            get
            {
                if (_DeclaringType != null) { return _DeclaringType; }
                // 确保动态增加的数据字段得到实体类型
                return Table.EntityType;
            }
            set { _DeclaringType = value; }
        }

        /// <summary>是否标识列</summary>
        public Boolean IsIdentity { get; internal set; }

        /// <summary>是否主键</summary>
        public Boolean PrimaryKey { get; internal set; }

        /// <summary>是否主字段。主字段作为业务主要字段,代表当前数据行意义</summary>
        public Boolean Master { get; private set; }

        /// <summary>是否允许空</summary>
        public Boolean IsNullable { get; internal set; }

        /// <summary>长度</summary>
        public Int32 Length { get; internal set; }

        /// <summary>是否数据绑定列</summary>
        public Boolean IsDataObjectField { get; set; }

        /// <summary>是否动态字段</summary>
        public Boolean IsDynamic => _Property == null;

        /// <summary>字段名要过滤掉的标识符,考虑MSSQL、MySql、SQLite、Oracle等</summary>
        static readonly Char[] COLUMNNAME_FLAG = new Char[] { '[', ']', '\'', '"', '`' };

        private String _ColumnName;
        /// <summary>用于数据绑定的字段名</summary>
        /// <remarks>
        /// 默认使用BindColumn特性中指定的字段名,如果没有指定,则使用属性名。
        /// 字段名可能两边带有方括号等标识符
        /// </remarks>
        public String ColumnName { get { return _ColumnName; } set { if (value != null) _ColumnName = value.Trim(COLUMNNAME_FLAG); } }

        /// <summary>是否只读</summary>
        /// <remarks>set { _ReadOnly = value; } 放出只读属性的设置,比如在编辑页面的时候,有的字段不能修改 如修改用户时  不能修改用户名</remarks>
        public Boolean ReadOnly { get; set; }

        /// <summary>表</summary>
        public TableItem Table { get; internal protected set; }

        /// <summary>字段</summary>
        public IDataColumn Field { get; private set; }

        /// <summary>实体操作者</summary>
        public IEntityOperate Factory
        {
            get
            {
                var type = Table.EntityType;
                if (type.IsInterface) return null;

                return EntityFactory.CreateOperate(type);
            }
        }

        /// <summary>已格式化的字段名,可字节用于SQL中。主要用于处理关键字,比如MSSQL里面的[User]</summary>
        public String FormatedName => Factory.FormatName(ColumnName);

        /// <summary>跟当前字段有关系的原始字段</summary>
        public FieldItem OriField { get; internal set; }

        /// <summary>获取映射特性</summary>
        public MapAttribute Map { get; private set; }
        #endregion

        #region 构造
        internal FieldItem() { }

        /// <summary>构造函数</summary>
        /// <param name="table"></param>
        /// <param name="property">属性</param>
        public FieldItem(TableItem table, PropertyInfo property)
        {
            Table = table;

            if (property != null)
            {
                _Property = property;
                var dc = _Column = BindColumnAttribute.GetCustomAttribute(property);
                var df = _DataObjectField = property.GetCustomAttribute<DataObjectFieldAttribute>();
                var ds = _Description = property.GetCustomAttribute<DescriptionAttribute>();
                var di = _DisplayName = property.GetCustomAttribute<DisplayNameAttribute>();
                Map = property.GetCustomAttribute<MapAttribute>();
                Name = property.Name;
                Type = property.PropertyType;
                DeclaringType = property.DeclaringType;

                if (df != null)
                {
                    IsIdentity = df.IsIdentity;
                    PrimaryKey = df.PrimaryKey;
                    IsNullable = df.IsNullable;
                    Length = df.Length;

                    IsDataObjectField = true;
                }

                if (dc != null)
                {
                    Master = dc.Master;
                }

                if (dc != null && !dc.Name.IsNullOrWhiteSpace())
                    ColumnName = dc.Name;
                else
                    ColumnName = Name;

                if (ds != null && !String.IsNullOrEmpty(ds.Description))
                    Description = ds.Description;
                else if (dc != null && !String.IsNullOrEmpty(dc.Description))
                    Description = dc.Description;
                if (di != null && !di.DisplayName.IsNullOrEmpty())
                    DisplayName = di.DisplayName;

                var map = Map;
                if (map == null || map.Provider == null) ReadOnly = !property.CanWrite;
                var ra = property.GetCustomAttribute<ReadOnlyAttribute>();
                if (ra != null) ReadOnly = ra.IsReadOnly;
            }
        }
        #endregion

        #region 方法
        /// <summary>已重载。</summary>
        /// <returns></returns>
        public override String ToString() => ColumnName;

        /// <summary>填充到XField中去</summary>
        /// <param name="field">字段</param>
        public void Fill(IDataColumn field)
        {
            Field = field;

            if (field == null) return;

            var dc = field;
            if (dc == null) return;

            dc.ColumnName = ColumnName;
            dc.Name = Name;
            dc.DataType = Type;
            dc.Description = Description;

            var col = _Column;
            if (col != null)
            {
                dc.RawType = col.RawType;
                dc.Precision = col.Precision;
                dc.Scale = col.Scale;
            }

            // 特别处理,兼容旧版本
            if (dc.DataType == typeof(Decimal))
            {
                if (dc.Precision == 0) dc.Precision = 19;
                if (dc.Scale == 0) dc.Scale = 4;
            }

            dc.Length = Length;
            dc.Identity = IsIdentity;
            dc.PrimaryKey = PrimaryKey;
            dc.Nullable = IsNullable;
            dc.Master = Master;
        }

        /// <summary>建立表达式</summary>
        /// <param name="format"></param>
        /// <param name="value">数值</param>
        /// <returns></returns>
        internal Expression CreateFormat(String format, Object value) => new FormatExpression(this, format, value);

        internal static Expression CreateField(FieldItem field, String action, Object value) => field == null ? new Expression() : new FieldExpression(field, action, value);
        #endregion

        #region 基本运算
        /// <summary>等于</summary>
        /// <param name="value">数值</param>
        /// <returns></returns>
        public Expression Equal(Object value) => CreateField(this, "=", value);

        /// <summary>不等于</summary>
        /// <param name="value">数值</param>
        /// <returns></returns>
        public Expression NotEqual(Object value) => CreateField(this, "<>", value);

        Expression CreateLike(String value) => CreateFormat("{0} Like {1}", value);

        /// <summary>以某个字符串开始,{0}%操作</summary>
        /// <remarks>空参数不参与表达式操作,不生成该部分SQL拼接</remarks>
        /// <param name="value">数值</param>
        /// <returns></returns>
        public Expression StartsWith(String value)
        {
            if (Type != typeof(String)) throw new NotSupportedException($"[{nameof(StartsWith)}]函数仅支持字符串字段!");

            if (value == null || value + "" == "") return new Expression();

            return CreateLike("{0}%".F(value));
        }

        /// <summary>以某个字符串结束,%{0}操作</summary>
        /// <remarks>空参数不参与表达式操作,不生成该部分SQL拼接</remarks>
        /// <param name="value">数值</param>
        /// <returns></returns>
        public Expression EndsWith(String value)
        {
            if (Type != typeof(String)) throw new NotSupportedException($"[{nameof(EndsWith)}]函数仅支持字符串字段!");

            if (value == null || value + "" == "") return new Expression();

            return CreateLike("%{0}".F(value));
        }

        /// <summary>包含某个字符串,%{0}%操作</summary>
        /// <remarks>空参数不参与表达式操作,不生成该部分SQL拼接</remarks>
        /// <param name="value">数值</param>
        /// <returns></returns>
        public Expression Contains(String value)
        {
            if (Type != typeof(String)) throw new NotSupportedException($"[{nameof(Contains)}]函数仅支持字符串字段!");

            if (value == null || value + "" == "") return new Expression();

            return CreateLike("%{0}%".F(value));
        }

        /// <summary>不包含某个字符串,%{0}%操作</summary>
        /// <remarks>空参数不参与表达式操作,不生成该部分SQL拼接</remarks>
        /// <param name="value">数值</param>
        /// <returns></returns>
        public Expression NotContains(String value)
        {
            if (Type != typeof(String)) throw new NotSupportedException($"[{nameof(NotContains)}]函数仅支持字符串字段!");

            if (value == null || value + "" == "") return new Expression();

            return CreateFormat("{0} Not Like {1}", "%{0}%".F(value));
        }

        /// <summary>In操作</summary>
        /// <remarks>空参数不参与表达式操作,不生成该部分SQL拼接。只有一项时转为等于</remarks>
        /// <param name="value">枚举数据,会转化为字符串</param>
        /// <returns></returns>
        public Expression In(IEnumerable value) => _In(value, true);

        Expression _In(IEnumerable value, Boolean flag)
        {
            if (value == null) return new Expression();

            var op = Factory;
            var name = op.FormatName(ColumnName);

            var vs = new List<Object>();
            var list = new List<Object>();
            foreach (var item in value)
            {
                // 避免重复项
                if (vs.Contains(item)) continue;
                vs.Add(item);

                // 格式化数值
                //var str = op.FormatValue(this, item);
                list.Add(item);
            }
            if (list.Count <= 0) return new Expression();

            // 特殊处理枚举全选,如果全选了枚举的所有项,则跳过当前条件构造
            if (vs[0].GetType().IsEnum)
            {
                var es = Enum.GetValues(vs[0].GetType());
                if (es.Length == vs.Count)
                {
                    if (vs.SequenceEqual(es.Cast<Object>())) return new Expression();
                }
            }

            // 如果In操作且只有一项,修改为等于
            if (list.Count == 1) return CreateField(this, flag ? "=" : "<>", vs[0]);

            return CreateFormat(flag ? "{0} In({1})" : "{0} Not In({1})", list);
        }

        /// <summary>NotIn操作</summary>
        /// <remarks>空参数不参与表达式操作,不生成该部分SQL拼接。只有一项时修改为不等于</remarks>
        /// <param name="value">数值</param>
        /// <returns></returns>
        public Expression NotIn(IEnumerable value) => _In(value, false);

        /// <summary>In操作。直接使用字符串可能有注入风险</summary>
        /// <remarks>空参数不参与表达式操作,不生成该部分SQL拼接</remarks>
        /// <param name="child">逗号分割的数据。可能有注入风险</param>
        /// <returns></returns>
        public Expression In(String child)
        {
            if (child == null) return new Expression();

            return CreateFormat("{0} In({1})", child);
        }

        /// <summary>NotIn操作。直接使用字符串可能有注入风险</summary>
        /// <remarks>空参数不参与表达式操作,不生成该部分SQL拼接</remarks>
        /// <param name="child">数值</param>
        /// <returns></returns>
        public Expression NotIn(String child)
        {
            if (child == null) return new Expression();

            return CreateFormat("{0} Not In({1})", child);
        }

        /// <summary>In操作。直接使用字符串可能有注入风险</summary>
        /// <remarks>空参数不参与表达式操作,不生成该部分SQL拼接</remarks>
        /// <param name="builder">逗号分割的数据。可能有注入风险</param>
        /// <returns></returns>
        public Expression In(SelectBuilder builder)
        {
            if (builder == null) return new Expression();

            return CreateFormat("{0} In({1})", builder);
        }

        /// <summary>NotIn操作。直接使用字符串可能有注入风险</summary>
        /// <remarks>空参数不参与表达式操作,不生成该部分SQL拼接</remarks>
        /// <param name="builder">数值</param>
        /// <returns></returns>
        public Expression NotIn(SelectBuilder builder)
        {
            if (builder == null) return new Expression();

            return CreateFormat("{0} Not In ({1})", builder);
        }

        /// <summary>IsNull操作,不为空,一般用于字符串,但不匹配0长度字符串</summary>
        /// <returns></returns>
        public Expression IsNull() => CreateFormat("{0} Is Null", null);

        /// <summary>NotIsNull操作</summary>
        /// <returns></returns>
        public Expression NotIsNull() => CreateFormat("Not {0} Is Null", null);
        #endregion

        #region 复杂运算
        /// <summary>IsNullOrEmpty操作,用于空或者0长度字符串</summary>
        /// <returns></returns>
        public Expression IsNullOrEmpty()
        {
            if (Type != typeof(String)) throw new NotSupportedException($"[{nameof(IsNullOrEmpty)}]函数仅支持字符串字段!");

            return IsNull() | Equal("");
        }

        /// <summary>NotIsNullOrEmpty操作</summary>
        /// <returns></returns>
        public Expression NotIsNullOrEmpty()
        {
            if (Type != typeof(String)) throw new NotSupportedException($"[{nameof(NotIsNullOrEmpty)}]函数仅支持字符串字段!");

            return NotIsNull() & NotEqual("");
        }

        /// <summary>是否True或者False/Null,参数决定两组之一</summary>
        /// <param name="flag"></param>
        /// <returns></returns>
        public Expression IsTrue(Boolean? flag)
        {
            if (Type != typeof(Boolean)) throw new NotSupportedException($"[{nameof(IsTrue)}]函数仅支持布尔型字段!");

            if (flag == null) return null;

            var f = flag.Value;
            if (f) return Equal(true);

            // IsTrue/IsFalse 不再需要判空,因为那样还不如直接使用等于号
            //if (Type == typeof(Boolean) && !IsNullable) return Equal(false);

            return NotEqual(true) | IsNull();
        }

        /// <summary>是否False或者True/Null,参数决定两组之一</summary>
        /// <param name="flag"></param>
        /// <returns></returns>
        public Expression IsFalse(Boolean? flag)
        {
            if (Type != typeof(Boolean)) throw new NotSupportedException($"[{nameof(IsFalse)}]函数仅支持布尔型字段!");

            if (flag == null) return null;

            var f = flag.Value;
            if (!f) return Equal(false);

            // IsTrue/IsFalse 不再需要判空,因为那样还不如直接使用等于号
            //if (Type == typeof(Boolean) && !IsNullable) return Equal(true);

            return NotEqual(false) | IsNull();
        }
        #endregion

        #region 重载运算符
        /// <summary>大于</summary>
        /// <param name="field">字段</param>
        /// <param name="value">数值</param>
        /// <returns></returns>
        public static Expression operator >(FieldItem field, Object value) => CreateField(field, ">", value);

        /// <summary>小于</summary>
        /// <param name="field">字段</param>
        /// <param name="value">数值</param>
        /// <returns></returns>
        public static Expression operator <(FieldItem field, Object value) => CreateField(field, "<", value);

        /// <summary>大于等于</summary>
        /// <param name="field">字段</param>
        /// <param name="value">数值</param>
        /// <returns></returns>
        public static Expression operator >=(FieldItem field, Object value) => CreateField(field, ">=", value);

        /// <summary>小于等于</summary>
        /// <param name="field">字段</param>
        /// <param name="value">数值</param>
        /// <returns></returns>
        public static Expression operator <=(FieldItem field, Object value) => CreateField(field, "<=", value);
        #endregion

        #region 类型转换
        /// <summary>类型转换</summary>
        /// <param name="obj"></param>
        /// <returns></returns>
        public static implicit operator String(FieldItem obj) => !obj.Equals(null) ? obj.ColumnName : null;
        #endregion
    }

    /// <summary>继承FieldItem,仅仅为了重载==和!=运算符</summary>
    [EditorBrowsable(EditorBrowsableState.Never)]
    public class Field : FieldItem
    {
        #region 构造
        /// <summary>构造函数</summary>
        /// <param name="table"></param>
        /// <param name="property">属性</param>
        public Field(TableItem table, PropertyInfo property) : base(table, property) { }

        internal Field(TableItem table, String name, Type type, String description, Int32 length)
        {
            Table = table;

            Name = name;
            ColumnName = name;
            Type = type;
            Description = description;
            Length = length;

            IsNullable = true;
        }
        #endregion

        /// <summary>等于</summary>
        /// <param name="field">字段</param>
        /// <param name="value">数值</param>
        /// <returns></returns>
        public static Expression operator ==(Field field, Object value) => field.Equal(value);

        /// <summary>不等于</summary>
        /// <param name="field">字段</param>
        /// <param name="value">数值</param>
        /// <returns></returns>
        public static Expression operator !=(Field field, Object value) => field.NotEqual(value);

        /// <summary>重写一下</summary>
        /// <returns></returns>
        public override Int32 GetHashCode() => base.GetHashCode();

        /// <summary>重写一下</summary>
        /// <param name="obj"></param>
        /// <returns></returns>
        public override Boolean Equals(Object obj) => base.Equals(obj);

        #region 类型转换
        /// <summary>类型转换</summary>
        /// <param name="obj"></param>
        /// <returns></returns>
        public static implicit operator String(Field obj) => !obj.Equals(null) ? obj.ColumnName : null;
        #endregion
    }
}