默认累加字段应该位于泛型基类中
nnhy authored at 2012-06-25 15:40:20
22.79 KiB
X
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.IO;
using System.Text;
using System.Xml.Serialization;
using NewLife.Collections;
using NewLife.IO;
using NewLife.Reflection;
using XCode.Common;

#if NET4
using System.Linq;
#else
using NewLife.Linq;
#endif

namespace XCode
{
    /// <summary>数据实体基类的基类</summary>
    [Serializable]
    [EditorBrowsable(EditorBrowsableState.Never)]
    public abstract partial class EntityBase : /*BinaryAccessor,*/ IEntity, ICloneable
    {
        #region 初始化数据
        /// <summary>首次连接数据库时初始化数据,仅用于实体类重载,用户不应该调用该方法</summary>
        [EditorBrowsable(EditorBrowsableState.Never)]
        internal protected virtual void InitData() { }
        #endregion

        #region 填充数据
        /// <summary>从一个数据行对象加载数据。不加载关联对象。</summary>
        /// <param name="dr">数据行</param>
        public abstract void LoadData(DataRow dr);

        /// <summary>从一个数据行对象加载数据。不加载关联对象。</summary>
        /// <param name="dr">数据读写器</param>
        public abstract void LoadDataReader(IDataReader dr);
        #endregion

        #region 操作
        /// <summary>把该对象持久化到数据库</summary>
        /// <returns></returns>
        public abstract Int32 Insert();

        /// <summary>更新数据库</summary>
        /// <returns></returns>
        public abstract Int32 Update();

        /// <summary>从数据库中删除该对象</summary>
        /// <returns></returns>
        public abstract Int32 Delete();

        /// <summary>保存。根据主键检查数据库中是否已存在该对象,再决定调用Insert或Update</summary>
        /// <returns></returns>
        public abstract Int32 Save();

        /// <summary>不需要验证的保存</summary>
        /// <returns></returns>
        public abstract Int32 SaveWithoutValid();
        #endregion

        #region 获取/设置 字段值
        /// <summary>
        /// 获取/设置 字段值。
        /// 一个索引,反射实现。
        /// 派生实体类可重写该索引,以避免发射带来的性能损耗。
        /// 基类已经实现了通用的快速访问,但是这里仍然重写,以增加控制,
        /// 比如字段名是属性名前面加上_,并且要求是实体字段才允许这样访问,否则一律按属性处理。
        /// </summary>
        /// <param name="name">字段名</param>
        /// <returns></returns>
        public abstract Object this[String name] { get; set; }

        /// <summary>设置字段值,该方法影响脏数据。</summary>
        /// <param name="name">字段名</param>
        /// <param name="value">值</param>
        /// <returns>返回是否成功设置了数据</returns>
        public Boolean SetItem(String name, Object value)
        {
            Boolean b = OnPropertyChanging(name, value);
            if (b)
            {
                // OnPropertyChanging中根据新旧值是否相同来影响脏数据
                // SetItem作为必定影响脏数据的代替者
                this[name] = value;
                Dirtys[name] = true;
            }
            return b;
        }
        #endregion

        #region 导入导出XML
        /// <summary>建立Xml序列化器</summary>
        /// <returns></returns>
        //[Obsolete("该成员在后续版本中将不再被支持!")]
        protected virtual XmlSerializer CreateXmlSerializer()
        {
            return new XmlSerializer(this.GetType());
        }

        /// <summary>导出XML</summary>
        /// <returns></returns>
        [Obsolete("该成员在后续版本中将不再被支持!请使用实体访问器IEntityAccessor替代!")]
        public virtual String ToXml()
        {
            XmlSerializer serial = CreateXmlSerializer();
            using (MemoryStream stream = new MemoryStream())
            {
                StreamWriter writer = new StreamWriter(stream, Encoding.UTF8);
                serial.Serialize(writer, this);
                Byte[] bts = stream.ToArray();
                String xml = Encoding.UTF8.GetString(bts);
                writer.Close();
                if (!String.IsNullOrEmpty(xml)) xml = xml.Trim();
                return xml;
            }
        }
        #endregion

        #region 导入导出Json
        /// <summary>导出Json</summary>
        /// <returns></returns>
        [Obsolete("该成员在后续版本中将不再被支持!")]
        public virtual String ToJson()
        {
            Json json = new Json();
            return json.Serialize(this);
        }
        #endregion

        #region 克隆
        /// <summary>创建当前对象的克隆对象,仅拷贝基本字段</summary>
        /// <returns></returns>
        public abstract Object Clone();

        /// <summary>克隆实体。创建当前对象的克隆对象,仅拷贝基本字段</summary>
        /// <param name="setDirty">是否设置脏数据</param>
        /// <returns></returns>
        IEntity IEntity.CloneEntity(Boolean setDirty) { return CloneEntityInternal(setDirty); }

        /// <summary>克隆实体</summary>
        /// <param name="setDirty"></param>
        /// <returns></returns>
        internal protected abstract IEntity CloneEntityInternal(Boolean setDirty = true);

        /// <summary>复制来自指定实体的成员,可以是不同类型的实体,只复制共有的基本字段,影响脏数据</summary>
        /// <param name="entity">来源实体对象</param>
        /// <param name="setDirty">是否设置脏数据</param>
        /// <returns>实际复制成员数</returns>
        public virtual Int32 CopyFrom(IEntity entity, Boolean setDirty = true)
        {
            IEntity src = this;
            var names1 = EntityFactory.CreateOperate(src.GetType()).FieldNames;
            if (names1 == null || names1.Count < 1) return 0;
            var names2 = EntityFactory.CreateOperate(entity.GetType()).FieldNames;
            if (names2 == null || names2.Count < 1) return 0;

            Int32 n = 0;
            foreach (var item in names1)
            {
                if (names2.Contains(item))
                {
                    if (setDirty)
                        src.SetItem(item, entity[item]);
                    else
                        src[item] = entity[item];

                    n++;
                }
            }
            // 赋值扩展数据
            if (entity.Extends != null)
            {
                foreach (var item in entity.Extends)
                {
                    src.Extends[item.Key] = item.Value;
                    if (setDirty) Dirtys[item.Key] = true;

                    n++;
                }
            }
            return n;
        }
        #endregion

        #region 脏数据
        [NonSerialized]
        private DirtyCollection _Dirtys;
        /// <summary>脏属性。存储哪些属性的数据被修改过了。</summary>
        [XmlIgnore]
        internal protected IDictionary<String, Boolean> Dirtys
        {
            get
            {
                if (_Dirtys == null) _Dirtys = new DirtyCollection();
                return _Dirtys;
            }
            //set { _Dirtys = value; }
        }

        /// <summary>脏属性。存储哪些属性的数据被修改过了。</summary>
        IDictionary<String, Boolean> IEntity.Dirtys { get { return Dirtys; } }

        /// <summary>设置所有数据的脏属性</summary>
        /// <param name="isDirty">改变脏属性的属性个数</param>
        /// <returns></returns>
        protected virtual Int32 SetDirty(Boolean isDirty)
        {
            if (_Dirtys == null || Dirtys.Count < 1) return 0;

            Int32 count = 0;
            foreach (String item in Dirtys.Keys)
            {
                if (Dirtys[item] != isDirty)
                {
                    Dirtys[item] = isDirty;
                    count++;
                }
            }
            return count;
        }

        /// <summary>属性改变。重载时记得调用基类的该方法,以设置脏数据属性,否则数据将无法Update到数据库。</summary>
        /// <param name="fieldName">字段名</param>
        /// <param name="newValue">新属性值</param>
        /// <returns>是否允许改变</returns>
        [Obsolete("改为使用OnPropertyChanging")]
        protected virtual Boolean OnPropertyChange(String fieldName, Object newValue)
        {
            //if (_PropertyChanging != null) _PropertyChanging(this, new PropertyChangingEventArgs(fieldName));
            //// 如果数据没有改变,不应该影响脏数据
            ////Dirtys[fieldName] = true;
            //if (!Object.Equals(this[fieldName], newValue)) Dirtys[fieldName] = true;
            //return true;

            return OnPropertyChanging(fieldName, newValue);
        }
        #endregion

        #region 扩展属性
        [NonSerialized]
        private DictionaryCache<String, Object> _Extends;
        /// <summary>扩展属性</summary>
        [XmlIgnore]
        [Browsable(false)]
        public DictionaryCache<String, Object> Extends
        {
            get { return _Extends ?? (_Extends = new DictionaryCache<String, Object>()); }
        }

        /// <summary>扩展属性</summary>
        IDictionary<String, Object> IEntity.Extends { get { return Extends; } }

        [NonSerialized]
        private Dictionary<Type, List<String>> _depends;
        /// <summary>类型依赖</summary>
        [XmlIgnore]
        private Dictionary<Type, List<String>> Depends
        {
            get { return _depends ?? (_depends = new Dictionary<Type, List<String>>()); }
        }

        /// <summary>改为线程静态,避免线程间干扰。注意初始化赋值对线程静态无效,只有第一个生效</summary>
        [ThreadStatic]
        private static Boolean? _StopExtend = false;
        /// <summary>是否停止扩展属性,停止扩展属性后,可以避免扩展属性自动触发获取数据的功能</summary>
        public static Boolean StopExtend
        {
            get
            {
                // 注意初始化赋值对线程静态无效,只有第一个生效
                if (_StopExtend == null) _StopExtend = false;
                return _StopExtend.Value;
            }
            set { _StopExtend = value; }
        }

        /// <summary>获取扩展属性,获取数据时向指定的依赖实体类注册数据更改事件</summary>
        /// <typeparam name="TDependEntity">依赖实体类,该实体类数据更改时清空所有依赖于实体类的扩展属性</typeparam>
        /// <typeparam name="TResult">返回类型</typeparam>
        /// <param name="key">键值</param>
        /// <param name="func">回调</param>
        /// <returns></returns>
        protected virtual TResult GetExtend<TDependEntity, TResult>(String key, Func<String, Object> func)
            where TDependEntity : Entity<TDependEntity>, new()
        {
            return GetExtend<TDependEntity, TResult>(key, func, true);
        }

        /// <summary>获取扩展属性,获取数据时向指定的依赖实体类注册数据更改事件</summary>
        /// <typeparam name="TDependEntity">依赖实体类,该实体类数据更改时清空所有依赖于实体类的扩展属性</typeparam>
        /// <typeparam name="TResult">返回类型</typeparam>
        /// <param name="key">键值</param>
        /// <param name="func">回调</param>
        /// <param name="cacheDefault">是否缓存默认值,可选参数,默认缓存</param>
        /// <returns></returns>
        protected virtual TResult GetExtend<TDependEntity, TResult>(String key, Func<String, Object> func, Boolean cacheDefault)
            where TDependEntity : Entity<TDependEntity>, new()
        {
            Object value = null;
            if (Extends.TryGetValue(key, out value)) return (TResult)value;

            if (StopExtend) return default(TResult);

            // 针对每个类型,仅注册一个事件
            Type type = typeof(TDependEntity);
            List<String> list = null;
            if (!Depends.TryGetValue(type, out list))
            {
                list = new List<String>();
                Depends.Add(type, list);
            }

            // 这里使用了成员方法GetExtend<TDependEntity>而不是匿名函数,为了避免生成包装类,且每次调用前实例化包装类带来较大开销
            return (TResult)Extends.GetItem<Func<String, Object>, List<String>>(key, func, list, new Func<String, Func<String, Object>, List<String>, Object>(GetExtend<TDependEntity>), cacheDefault);
        }

        Object GetExtend<TDependEntity>(String key, Func<String, Object> func, List<String> list) where TDependEntity : Entity<TDependEntity>, new()
        {
            //if (Database.Debug) Database.WriteLog("GetExtend({0}, {1})", key, this);

            //Func<String, Object> func = args[0] as Func<String, Object>;
            //List<String> list = args[1] as List<String>;

            Object value = null;
            if (func != null) value = func(key);
            if (!list.Contains(key)) list.Add(key);
            if (list.Count == 1)
            {
                // 这里使用RemoveExtend而不是匿名函数,为了避免生成包装类,事件的Target将指向包装类的实例,
                //而内部要对Target实行弱引用,就必须保证事件的Target是实体对象本身。
                // OnDataChange内部对事件进行了拆分,弱引用Target,反射调用Method,那样性能较低,所以使用了快速方法访问器MethodInfoEx,
                Entity<TDependEntity>.Meta.OnDataChange += RemoveExtend;
            }

            return value;
        }

        /// <summary>清理依赖于某类型的缓存</summary>
        /// <param name="dependType">依赖类型</param>
        void RemoveExtend(Type dependType)
        {
            // 停止扩展属性的情况下不生效
            if (StopExtend) return;

            if (Depends == null || Extends.Count < 1) return;
            // 找到依赖类型的扩展属性键值集合
            //List<String> list = Depends[dependType];
            List<String> list = null;
            if (!Depends.TryGetValue(dependType, out list) || list == null || list.Count < 1) return;

            lock (Extends)
            {
                // 清理该类型的所有扩展属性
                foreach (String key in list)
                {
                    //if (Extends.ContainsKey(key))
                    {
                        //if (Database.Debug)
                        //{
                        //    Object value = Extends[key];
                        //    Database.WriteLog("RemoveExtend({0}, {1}, {2})", key, this, value != null ? value.ToString() : "null");
                        //}
                        Extends.Remove(key);
                    }
                }
                list.Clear();
            }
        }

        /// <summary>设置扩展属性</summary>
        /// <typeparam name="TDependEntity"></typeparam>
        /// <param name="key"></param>
        /// <param name="value"></param>
        protected virtual void SetExtend<TDependEntity>(String key, Object value) where TDependEntity : Entity<TDependEntity>, new()
        {
            // 针对每个类型,仅注册一个事件
            Type type = typeof(TDependEntity);
            List<String> list = null;
            if (!Depends.TryGetValue(type, out list))
            {
                list = new List<String>();
                Depends.Add(type, list);
            }

            lock (Extends)
            {
                Extends[key] = value;
                if (!list.Contains(key)) list.Add(key);

                // 停止扩展属性的情况下不生效
                if (!StopExtend && list.Count == 1)
                {
                    Entity<TDependEntity>.Meta.OnDataChange += RemoveExtend;
                }
            }
        }
        #endregion

        #region 累加
        [NonSerialized]
        private IDictionary<String, Object> _Additions;

        /// <summary>设置累加字段。如果是第一次设置该字段,则保存该字段当前数据作为累加基础数据</summary>
        /// <param name="name">字段名称</param>
        /// <param name="reset">是否重置。可以保存当前数据作为累加基础数据</param>
        /// <returns>是否成功设置累加字段。如果不是第一次设置,并且没有重置数据,那么返回失败</returns>
        public Boolean SetAdditionalField(String name, Boolean reset = false)
        {
            // 检查集合是否为空
            if (_Additions == null)
            {
                //_Additions = new Dictionary<String, Object>(StringComparer.OrdinalIgnoreCase);
                _Additions = new Dictionary<String, Object>();
            }

            lock (_Additions)
            {
                if (reset || !_Additions.ContainsKey(name))
                {
                    _Additions[name] = this[name];
                    return true;
                }
                else
                    return false;
            }
        }

        /// <summary>删除累加字段。</summary>
        /// <param name="name">字段名称</param>
        /// <param name="restore">是否恢复数据</param>
        /// <returns>是否成功删除累加字段</returns>
        public Boolean RemoveAdditionalField(String name, Boolean restore = false)
        {
            if (_Additions == null) return false;

            Object obj = null;
            if (!_Additions.TryGetValue(name, out obj)) return false;

            if (restore) this[name] = obj;

            return true;
        }

        /// <summary>尝试获取累加数据</summary>
        /// <param name="name">字段名称</param>
        /// <param name="value">累加数据绝对值</param>
        /// <param name="sign">正负</param>
        /// <returns>是否获取指定字段的累加数据</returns>
        public Boolean TryGetAdditionalValue(String name, out Object value, out Boolean sign)
        {
            value = null;
            sign = true;
            if (_Additions == null) return false;

            if (!_Additions.TryGetValue(name, out value)) return false;

            // 计算累加数据
            var current = this[name];
            var type = current.GetType();
            var code = Type.GetTypeCode(type);
            switch (code)
            {
                case TypeCode.Char:
                case TypeCode.Byte:
                case TypeCode.SByte:
                case TypeCode.Int16:
                case TypeCode.Int32:
                case TypeCode.Int64:
                case TypeCode.UInt16:
                case TypeCode.UInt32:
                case TypeCode.UInt64:
                    {
                        var v = Convert.ToInt64(current) - Convert.ToInt64(value);
                        if (v < 0)
                        {
                            v *= -1;
                            sign = false;
                        }
                        //value = Convert.ChangeType(v, type);
                        value = v;
                    }
                    break;
                case TypeCode.Single:
                    {
                        var v = (Single)current - (Single)value;
                        if (v < 0)
                        {
                            v *= -1;
                            sign = false;
                        }
                        value = v;
                    }
                    break;
                case TypeCode.Double:
                    {
                        var v = (Double)current - (Double)value;
                        if (v < 0)
                        {
                            v *= -1;
                            sign = false;
                        }
                        value = v;
                    }
                    break;
                case TypeCode.Decimal:
                    {
                        var v = (Decimal)current - (Decimal)value;
                        if (v < 0)
                        {
                            v *= -1;
                            sign = false;
                        }
                        value = v;
                    }
                    break;
                default:
                    break;
            }

            return true;
        }

        /// <summary>清除累加字段数据。Update后调用该方法</summary>
        public void ClearAdditionalValues()
        {
            if (_Additions == null) return;

            foreach (var item in _Additions.Keys.ToArray())
            {
                _Additions[item] = this[item];
            }
        }
        #endregion

        #region 主键为空
        /// <summary>主键是否为空</summary>
        Boolean IEntity.IsNullKey
        {
            get
            {
                return Helper.IsEntityNullKey(this);
            }
        }

        /// <summary>设置主键为空。Save将调用Insert</summary>
        void IEntity.SetNullKey()
        {
            IEntityOperate eop = EntityFactory.CreateOperate(GetType());
            foreach (var item in eop.Fields)
            {
                if (item.PrimaryKey || item.IsIdentity) this[item.Name] = null;
            }
        }
        #endregion

        #region 实体相等
        /// <summary>判断两个实体是否相等。有可能是同一条数据的两个实体对象</summary>
        /// <param name="entity"></param>
        /// <returns></returns>
        Boolean IEntity.EqualTo(IEntity entity)
        {
            if (entity == null || this.GetType() != entity.GetType()) return false;
            if (this == entity) return true;

            // 判断是否所有主键相等
            var op = EntityFactory.CreateOperate(this.GetType());
            foreach (var item in op.Table.PrimaryKeys)
            {
                Object v1 = this[item.Name];
                Object v2 = entity[item.Name];
                if (item.Type == typeof(String)) { v1 += ""; v2 += ""; }

                if (!Object.Equals(v1, v2)) return false;
            }

            return true;
        }
        #endregion
    }
}