程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> C# >> C#入門知識 >> 使用AOP改進Fireasy實體模型,aopfireasy實體模型

使用AOP改進Fireasy實體模型,aopfireasy實體模型

編輯:C#入門知識

使用AOP改進Fireasy實體模型,aopfireasy實體模型


      目前Fireasy中的實體模型雖然用CodeBuilder生成,但是還是比較龐大,手工維護起來比較吃力,因此一直想使用比較簡單的一種模型,比如屬性只有 get 和 set,最多加個ColumnAttribute之類的來做映射。但這樣做的話,屬性修改這一特性就無法保持了,即更新實體時不知道哪些屬性有改動過。

      我們先看一下目前生成的實體類代碼:

    /// <summary>
    /// 部門 實體類。
    /// </summary>
    [Serializable]
    [EntityMapping("TB_DEPT", Description = "部門")]
    public partial class Dept : EntityObject
    {
        #region Static Property Definition
        
        /// <summary>
        /// ID的依賴屬性。
        /// </summary>
        public static readonly IProperty EpId = PropertyUnity.RegisterProperty<Dept>(s => s.Id, new PropertyMapInfo { IsPrimaryKey = true, IsNullable = false, Length = 36, Description = "ID", FieldName = "ID" });

        /// <summary>
        /// 編碼的依賴屬性。
        /// </summary>
        public static readonly IProperty EpNo = PropertyUnity.RegisterProperty<Dept>(s => s.No, new PropertyMapInfo { Length = 50, Description = "編碼", FieldName = "NO" });

        /// <summary>
        /// 名稱的依賴屬性。
        /// </summary>
        public static readonly IProperty EpName = PropertyUnity.RegisterProperty<Dept>(s => s.Name, new PropertyMapInfo { Length = 50, Description = "名稱", FieldName = "NAME" });

        /// <summary>
        /// 排序的依賴屬性。
        /// </summary>
        public static readonly IProperty EpOrderNo = PropertyUnity.RegisterProperty<Dept>(s => s.OrderNo, new PropertyMapInfo { Description = "排序", FieldName = "ORDER_NO" });

        #endregion

        #region Properties
        
        /// <summary>
        /// 獲取或設置ID。
        /// </summary>
        public string Id
        {
            get { return (string)GetValue(EpId); }
            set { SetValue(EpId, value); }
        }

        /// <summary>
        /// 獲取或設置編碼。
        /// </summary>
        public string No
        {
            get { return (string)GetValue(EpNo); }
            set { SetValue(EpNo, value); }
        }

        /// <summary>
        /// 獲取或設置名稱。
        /// </summary>
        public string Name
        {
            get { return (string)GetValue(EpName); }
            set { SetValue(EpName, value); }
        }

        /// <summary>
        /// 獲取或設置排序。
        /// </summary>
        public int? OrderNo
        {
            get { return (int?)GetValue(EpOrderNo); }
            set { SetValue(EpOrderNo, value); }
        }

        #endregion

    }

      以上的實體模型,主要借鑒WPF中的依賴屬性原理,定義一個靜態的Field實現關系映射,最主要的一點,就是在設置屬性的時候,交由基類的SetValue方法來執行,它會把這個修改過的屬性記下來,在更新實體的時候,達到只更新修改過的屬性的目的。

      而GetValue和SetValue看似有裝箱和拆箱操作,實際上屬性的值是以PropertyValue這個結構存儲的,通過重載強制轉換和顯式轉換操作符來交換數據,所以這裡不用擔心啦。

      那麼,實際上有了EntityObject基類就好辦多了,可以再抽象出一個類,讓它基於AOP來創建,在設置屬性值之後和獲取屬性值之後分別調用SetValue和GetValue就好了。

      Fireasy本來就提供了AOP,在Fireasy.Common.Aop命名空間中,關鍵是IInterceptor這個接口,它通過InterceptAttribute特性來綁定要攔截的方法、屬性或是整個類。

      還有一個接口IAopSupport,它只起到一個標識,在擴展方法New中,如果類型實現了IAopSupport,那麼則使用AspectFactory來創建對象。

      好,接下來我們定義一個 LighEntityObject 類:

    [Intercept(typeof(LighEntityInterceptor))]
    public abstract class LighEntityObject<TEntity> : EntityObject, 
        IAopSupport, 
        IEntityPropertyInitialize
        where TEntity : IEntity
    {
        /// <summary>
        /// 構造一個代理對象。
        /// </summary>
        /// <returns></returns>
        public static TEntity New()
        {
            return typeof(TEntity).New<TEntity>();
        }

        void IEntityPropertyInitialize.Initialize()
        {
            var entityType = this.GetType();

            foreach (var property in entityType.BaseType.GetProperties(BindingFlags.Public | BindingFlags.Instance))
            {
                if (property.DeclaringType == entityType.BaseType)
                {
                    //定義為 virtual
                    var getMth = property.GetGetMethod();
                    if (getMth != null && getMth.IsVirtual && !getMth.IsFinal)
                    {
                        RegisterProperty(entityType.BaseType, property);
                    }
                }
            }
        }

        private void RegisterProperty(Type entityType, PropertyInfo property)
        {
            //關聯屬性,即關聯實體或子實體集屬性
            if (typeof(IEntity).IsAssignableFrom(property.PropertyType) ||
                typeof(IEntitySet).IsAssignableFrom(property.PropertyType))
            {
                var mapping = property.GetCustomAttributes<PropertyMappingAttribute>().FirstOrDefault();
                var options = mapping != null && mapping.GetFlag(PropertyMappingAttribute.SetMark.LoadBehavior) ?
                    new RelationOptions(mapping.LoadBehavior) : null;

                PropertyUnity.RegisterSupposedProperty(property.Name, property.PropertyType, entityType, options: options);
            }
            else
            {
                var gp = new GeneralProperty()
                    {
                        Name = property.Name,
                        Type = property.PropertyType,
                        EntityType = entityType,
                        Info = new PropertyMapInfo { ReflectionInfo = property, FieldName = property.Name }
                    };

                var mapping = property.GetCustomAttributes<PropertyMappingAttribute>().FirstOrDefault();
                if (mapping != null)
                {
                    InitMapInfo(mapping, gp.Info);
                }

                PropertyUnity.RegisterProperty(entityType, gp);
            }
        }

        /// <summary>
        /// 根據映射特性設置屬性的映射信息。
        /// </summary>
        /// <param name="mapping"></param>
        /// <param name="mapInfo"></param>
        private void InitMapInfo(PropertyMappingAttribute mapping, PropertyMapInfo mapInfo)
        {
            mapInfo.FieldName = mapping.ColumnName;
            mapInfo.Description = mapping.Description;
            mapInfo.GenerateType = mapping.GenerateType;

            if (mapping.GetFlag(PropertyMappingAttribute.SetMark.DataType))
            {
                mapInfo.DataType = mapping.DataType;
            }

            if (mapping.GetFlag(PropertyMappingAttribute.SetMark.IsPrimaryKey))
            {
                mapInfo.IsPrimaryKey = mapping.IsPrimaryKey;
            }

            if (mapping.GetFlag(PropertyMappingAttribute.SetMark.IsDeletedKey))
            {
                mapInfo.IsDeletedKey = mapping.IsDeletedKey;
            }

            if (mapping.DefaultValue != null)
            {
                mapInfo.DefaultValue = PropertyValue.New(mapping.DefaultValue, mapInfo.ReflectionInfo.PropertyType);
            }

            if (mapping.GetFlag(PropertyMappingAttribute.SetMark.Length))
            {
                mapInfo.Length = mapping.Length;
            }

            if (mapping.GetFlag(PropertyMappingAttribute.SetMark.Precision))
            {
                mapInfo.Precision = mapping.Precision;
            }

            if (mapping.GetFlag(PropertyMappingAttribute.SetMark.Scale))
            {
                mapInfo.Scale = mapping.Scale;
            }
        }
    }

      關鍵還是攔截器了,看下面代碼:

    public sealed class LighEntityInterceptor : IInterceptor
    {
        void IInterceptor.Initialize(InterceptContext context)
        {
        }

        void IInterceptor.Intercept(InterceptCallInfo info)
        {
            if (info.InterceptType == InterceptType.AfterGetValue ||
                info.InterceptType == InterceptType.AfterSetValue)
            {
                var entity = info.Target as EntityObject;
                var entityType = entity.EntityType;
                var property = PropertyUnity.GetProperty(entityType, info.Member.Name);
                if (property == null)
                {
                    return;
                }

                switch (info.InterceptType)
                {
                    case InterceptType.AfterGetValue:
                        var value = entity.GetValue(property);
                        if (value != null && !value.IsEmpty)
                        {
                            info.ReturnValue = value.GetStorageValue();
                        }
                        break;
                    case InterceptType.AfterSetValue:
                        entity.SetValue(property, info.Arguments[0]);
                        break;
                }
            }
        }
    }

      其實很簡單,在讀取屬性值和設置屬性值的時候,調用GetValue和SetValue就可以了,是不是超簡單哈。

      LighEntityObject定義好了,但是在何時使用AOP生成代理類呢。

      在QueryBinder中找到VisitConstant方法,加入下面這一段:

        protected override Expression VisitConstant(ConstantExpression c)
        {
            if (IsQueryable(c))
            {
                var rowType = c.Type.GetEnumerableElementType();
                if (typeof(IEntity).IsAssignableFrom(rowType))
                {
                    if (typeof(IAopSupport).IsAssignableFrom(rowType))
                    {
                        rowType = Fireasy.Common.Aop.InterceptBuilder.BuildType(rowType);
                    }

                    return VisitSequence(QueryUtility.GetTableQuery(EntityMetadataUnity.GetEntityMetadata(rowType)));
                }
                else
                {
                    var q = (IQueryable)c.Value;
                    return base.Visit(q.Expression);
                }
            }
            return c;
        }

 

      好了,改造完成,現在的實體模型比較清爽多了:

    /// <summary>
    /// 部門 實體類。
    /// </summary>
    [Serializable]
    [EntityMapping("TB_DEPT", Description = "部門")]
    public partial class Dept : LighEntityObject<Dept>
    {
        
        /// <summary>
        /// 獲取或設置ID。
        /// </summary>
        [PropertyMapping(ColumnName = "ID", Description = "ID", IsPrimaryKey = true, IsNullable = false, Length = 36)]
        public virtual string Id { get; set; }

        /// <summary>
        /// 獲取或設置編碼。
        /// </summary>
        [PropertyMapping(ColumnName = "NO", Description = "編碼", Length = 50)]
        public virtual string No { get; set; }

        /// <summary>
        /// 獲取或設置名稱。
        /// </summary>
        [PropertyMapping(ColumnName = "NAME", Description = "名稱", Length = 50)]
        public virtual string Name { get; set; }

        /// <summary>
        /// 獲取或設置排序。
        /// </summary>
        [PropertyMapping(ColumnName = "ORDER_NO", Description = "排序")]
        public virtual int? OrderNo { get; set; }

        
        /// <summary>
        /// 獲取或設置 <see cref="Employee"/> 的子實體集。
        /// </summary>
        public virtual EntitySet<Employee> Employees { get; set; }

    }

      注意啰,屬性都要定義成virtual,不然AOP是不會識別的。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved