使用AOP改进Fireasy实体模型

目前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是不会识别的。

时间: 2024-11-05 12:24:35

使用AOP改进Fireasy实体模型的相关文章

责任链模式进阶:与AOP思想的融合与应用

摘要: AOP的理念可以很容易抽象出横切关注点,基于AOP理念我们可以将责任链模式中各具体处理角色中共同的实现责任链结构的行为抽象出来并将其模块化,以便进一步提高代码复用率和系统可维护性.实际上,无论是Java Web中的过滤器,还是Struts2中的Interceptor,它们都是责任链模式与AOP思想互相融合的巧妙实践.为了更进一步理解AOP (Aspect-Oriented Programming,AOP) 和 CoR (Chain of Responsibility),本文还概述了Fil

Fireasy

Fireasy与Asp.net MVC结合 Fireasy之前都是使用HttpService来为jquery ajax提供服务,这个HttpService实际上和MVC的原理机制是一样的,只是它支持两种方式,一种是使用统一的一个类来提供服务(基于MEF导入),另一种是使用aspx的类文件提供服务,具体使用哪一种,根据项目的性质来决定. Asp.net MVC也就了解了一些皮毛,还不是很熟悉,正在深度学习中.不过基于以前的开发习惯,我觉得MVC要进行以下几点的改进: (1)异常处理.MVC实现了一

Fireasy版本发布 1.5.40.42030

开发指南 代码生成 1.5.40.42030  2015-4-1 ** Fireasy.Common1.完善To方法,可以对可枚举类型进行转换2.完善Json序列化对动态类型的支持 ** Fireasy.Data3.增加Update方法的另一个版本 ** Fireasy.Data.Entity4.增强Linq扩展方法Order和ThenBy5.实体增加All扩展方法,可以简便返回所有属性6.仓储增加Include.Associate和Batch方法,EntityContext增加Apply方法7

spring AOP 实现事务和主从读写分离

1 切面 是个类 2 切入点 3 连接点 4 通知 是个方法 5 配置文件 <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context=&q

AOP

AOP 编辑删除转载 2015-12-08 16:14:27 标签:aop日志系能分析 C++11实现一个轻量级的AOP框架 AOP介绍 AOP(Aspect-Oriented Programming,面向方面编程),可以解决面向对象编程中的一些问题,是OOP的一种有益补充.面向对象编程中的继承是一种从上而下的关系,不适 合定义从左到右的横向关系,如果继承体系中的很多无关联的对象都有一些公共行为,这些公共行为可能分散在不同的组件.不同的对象之中,通过继承方式提取这 些公共行为就不太合适了.使用A

细说 Fireasy Entity Linq解析的几个独创之处

Fireasy Entity的linq内核解析是参考自iqtoolkit源码的,作者熟读源码并吸收其博大精深的思想后,结合项目中的一些需求,对它进行了几处改进.      一.逻辑删除标记 做管理系统的开发者可能会习惯于对数据做逻辑删除处理,即将数据打这一个标记,查询的时候将这些数据过滤掉.在Fireasy Entity的元数据定义PropertyMapInfo类中,有IsDeletedKey这样一个属性,表示使用此列作为逻辑删除标记.对应的,在查询数据的时候,需要把这个标记拼到LINQ里去,不

【SpringMVC笔记】第五课 改进Handler处理器和视图解析器

第四课 已经对注解的映射器和适配器进行了改进. 接下来需要对Handler处理器和视图解析器进行改进. <!-- 配置handler处理器 --> <bean class="com.king.controller.UserController"></bean> <!-- 配置视图解析器 --> <bean class="org.springframework.web.servlet.view.InternalResourc

2015第29周二AOP

1.问题:想要添加日志记录.性能监控.安全监测 2.最初解决方案 2.1.最初解决方案:在每个需要的类函数中重复写上面处理代. 缺点:太多重复代码,且紧耦合 2.2.抽象类进行共性设计,子类进行个性设计,此处不讲解,缺点一荣俱荣,一损俱损 2.3.使用装饰器模式/代理模式改进的解决方案 装饰器模式:动态地给一个对象添加一些额外的职责.就增加功能来说, 装饰器模式相比生成子类更为灵活. 代理模式:为其他对象提供一种代理以控制对这个对象的访问. 缺点:紧耦合,每个业务逻辑需要一个装饰器实现或代理 2

spring aop拦截controller方法

背景 开发的web应用程序涉及到校验采用的spring校验框架,在controller的方法中到处都要写校验处理,异常处理,能否减少这部分冗余代码. 问题: 这是表单提交的处理 1 @RequestMapping(value = "/edit", method = RequestMethod.POST) 2 public String edit(@Valid FormBean formBean, BindingResult bindingResult, Model model) { 3