Lind.DDD~实体属性变更追踪器的实现

回到目录

看着这个标题很复杂,大叔把它拆开说一下,实体属性-变更-追踪器,把它拆成三部分大家看起来就容易懂一些了,实体属性:领域实体里有自己的属性,属性有getter,setter块,用来返回和设置属性的内容;变更:当前属性为赋值时,我们对它进行监视;追踪器:对变量的内容进行处理。好了,我们回到Lind.DDD框架中,在框架里有领域实体基类EntityBase,这个类是所有实体的基类,它公开了一些属性和方法,我们对这个基类进行一些设置,让所有子类都继承它,享用它。

1 属性变更追踪接口和它的事件

    // 摘要:
    //     向客户端发出某一属性值已更改的通知。
    public interface INotifyPropertyChanged
    {
        // 摘要:
        //     在更改属性值时发生。
        event PropertyChangedEventHandler PropertyChanged;
    }

2 基类EntityBase,添加了事件和它的方法,及触发事件的方法

    /// <summary>
    /// 领域模型,实体模型基类,它可能有多种持久化方式,如DB,File,Redis,Mongodb,XML等
    /// Lind.DDD框架的领域模型与数据库实体合二为一
    /// </summary>
    [PropertyChangedAttribute]
    public abstract class EntityBase : ContextBoundObject, IEntity, INotifyPropertyChanged
    {
        /// <summary>
        /// 实体初始化
        /// </summary>
        public EntityBase()
        {
            this.Status = Status.Normal;
            this.UpdateDateTime = DateTime.Now;
            this.CreateDateTime = DateTime.Now;
            this.PropertyChanged += EntityBase_PropertyChanged;
        }

        /// <summary>
        /// 建立时间
        /// </summary>
        [XmlIgnore, DataMember(Order = 3), XmlElement(Order = 3), DisplayName("建立时间"), Column("CreateTime"), Required]
        public DateTime CreateDateTime { get; set; }
        /// <summary>
        /// 更新时间
        /// </summary>
        [XmlIgnore, DataMember(Order = 2), XmlElement(Order = 2), DisplayName("更新时间"), Column("UpdateTime"), Required]
        public DateTime UpdateDateTime { get; set; }
        /// <summary>
        /// 实体状态
        /// </summary>
        [XmlIgnore, DataMember(Order = 1), XmlElement(Order = 1), DisplayName("状态"), Required]
        public Status Status { get; set; }

        /// <summary>
        /// 拿到实体验证的结果列表
        /// 结果为null或者Enumerable.Count()==0表达验证成功
        /// </summary>
        /// <returns></returns>
        public IEnumerable<RuleViolation> GetRuleViolations()
        {
            var properties = this.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance).ToArray();

            foreach (var i in properties)
            {
                var attr = i.GetCustomAttributes();
                foreach (var a in attr)
                {
                    var val = (a as ValidationAttribute);
                    if (val != null)
                        if (!val.IsValid(i.GetValue(this)))
                        {
                            yield return new RuleViolation(val.ErrorMessage, i.Name);
                        }
                }
            }

        }

        #region PropertyChangedEventHandler Events
        /// <summary>
        /// 属性值变更事件
        /// </summary>
        public event PropertyChangedEventHandler PropertyChanged;
        /// <summary>
        /// 事件实例
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void EntityBase_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            Console.WriteLine("属性:{0},值:{1}", e.PropertyName, sender.GetType().GetProperty(e.PropertyName).GetValue(sender));
        }
        /// <summary>
        /// 触发事件,写在每个属性的set块中CallerMemberName特性表示当前块的属性名
        /// </summary>
        /// <param name="propertyName"></param>
        public void OnPropertyChanged([CallerMemberName] string propertyName = null)
        {
            if (PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }

        #endregion

    }

3 定义变更拦截器特性

    /// <summary>
    /// 类中方法拦截的特性
    /// </summary>
    public class PropertyChangedAttribute : ProxyAttribute
    {
        public override MarshalByRefObject CreateInstance(Type serverType)
        {
            PropertyChangedProxy realProxy = new PropertyChangedProxy(serverType);
            return realProxy.GetTransparentProxy() as MarshalByRefObject;
        }
    }

4 实现拦截器功能

    /// <summary>
    /// 属性变更拦截器
    /// </summary>
    public class PropertyChangedProxy : RealProxy
    {
        Type serverType;
        public PropertyChangedProxy(Type serverType)
            : base(serverType)
        {
            this.serverType = serverType;
        }
        public override IMessage Invoke(IMessage msg)
        {
            //构造方法
            if (msg is IConstructionCallMessage)
            {
                IConstructionCallMessage constructCallMsg = msg as IConstructionCallMessage;
                IConstructionReturnMessage constructionReturnMessage = this.InitializeServerObject((IConstructionCallMessage)msg);
                RealProxy.SetStubData(this, constructionReturnMessage.ReturnValue);
                return constructionReturnMessage;
            }
            //其它方法(属性也是方法,它会被翻译成set_property,get_property,类似于java里的属性封装)
            else if (msg is IMethodCallMessage)
            {

                IMethodCallMessage callMsg = msg as IMethodCallMessage;
                object[] args = callMsg.Args;
                IMessage message;
                try
                {

                    if (callMsg.MethodName.StartsWith("set_") && args.Length == 1)
                    {
                        string propertyName = Regex.Split(callMsg.MethodName, "set_")[1];
                        //这里检测到是set方法,然后应怎么调用对象的其它方法呢?
                        var method = this.serverType.GetMethod("OnPropertyChanged");
                        if (method != null)
                        {
                            var obj = GetUnwrappedServer();
                            obj.GetType().GetProperty(propertyName).SetValue(obj, args.FirstOrDefault());
                            method.Invoke(obj, new object[] { propertyName });//这块对象为空了
                        }

                    }

                    object o = callMsg.MethodBase.Invoke(GetUnwrappedServer(), args);
                    message = new ReturnMessage(o, args, args.Length, callMsg.LogicalCallContext, callMsg);
                }

                catch (Exception e)
                {

                    message = new ReturnMessage(e, callMsg);

                }

                return message;

            }

            return msg;

        }
    }

5 总结

本例子主要让大家了解了事件,事件触发机制,AOP拦截技术等知识点,而且通过本例子,我们可以对类的属性进行监视,并订阅一些方法来处理这些变更行为!下面这个代码是最简单的属性变更的记录,本user对象为赋值时,它的两个被set的属性成为了监视的对象

     User u1 = new User();
     u1.UserName = "OK";
     u1.Age = 100;

回到目录

时间: 2024-08-25 14:21:00

Lind.DDD~实体属性变更追踪器的实现的相关文章

Lind.DDD.Aspects通过Plugins实现方法的动态拦截~Lind里的AOP

回到目录 .Net MVC之所以发展的如些之好,一个很重要原因就是它公开了一组AOP的过滤器,即使用这些过滤器可以方便的拦截controller里的action,并注入我们自己的代码逻辑,向全局的异常记录,用户授权,Url授权,操作行为记录等,这一大批Lind的基本组件都是实现MVC和API的过滤实现的,使用这些过滤让我们不用去像HttpModule和HttpHandler那样,还要在Config里配置注入点,让程序员在开发方式上感觉很舒服,维护成功很低! 本文主要内容点 Lind.DDD里的方

Lind.DDD.Domain.IOwnerBehavor对实体的意义

回到目录 对于Lind.DDD架构,我之前写了不少文章,对于它的Domain模式也介绍了不少,像之前的IEntity,ILogicDeleteBehavor,IModifyBehavor,IStatusBehavor和ISortBehavor都有自己的功能,只要实体实现对外的接口,就具有了某种特性或者某种功能,而今天主要说一下拥有者接口,IOwnerBehavor,它主要用在管理系统的实体中,如一个员工资产表,当这个员工离职后,它对应资产将被进行转移,转移到一个新的用户身上,而这个用户就是这个资

Lind.DDD.Domain领域模型介绍

回到目录 Lind.DDD.Domain位于Lind.DDD核心项目中,它主要面向领域实体而设计,由一个IEntity的标识接口,EntityBase基类和N个Entity实体类组成,其中IEntity主要用来标识,在仓储操作时,用它来表明操作的实体范围和约束:EntityBase定义了几个公用的属性,为了避免代码的重复,特意将状态,插入时间和更新时间定义到了EntityBase里,而为何不将主键定义进来呢,主要考虑到主键的类型是为确实的,还有就是不同类型的主键可能需要实现不同的特性,如Mong

Lind.DDD敏捷领域驱动框架~Lind.DDD各层介绍

回到目录 Lind.DDD项目主要面向敏捷,快速开发,领域驱动等,对于它的分层也是能合并的合并,比之前大叔的框架分层更粗糙一些,或者说更大胆一些,在开发人员使用上,可能会感觉更方便了,更益使用了,这就是大叔开发Lind.DDD框架的目的,让一切变得更简单... Lind.DDD层 主要是公用方法,组件,规约等,如日志组件(Logger),消息组件(Messaging),IOC,AOP,缓存(Caching),异常,请求/响应,用户授权(Authorization),安全校验,领域模型(Domai

Lind.DDD.Repositories.Mongo层介绍

回到目录 之前已经发生了 大叔之前讲过被仓储化了的Mongodb,而在大叔开发了Lind.DDD之后,决定把这个东西再搬到本框架的仓储层来,这也是大势所趋的,毕竟mongodb是最像关系数据库的NoSql,它的使用场景是其它nosql所不能及的,这点是毋庸置疑的! 下面是大叔总结的Mongodb文章目录,选自<大叔Mongodb系列> MongoDB学习笔记~环境搭建 (2015-03-30 10:34) MongoDB学习笔记~MongoDBRepository仓储的实现 (2015-04-

Lind.DDD.IoC(大叔推荐)~在服务定位器中引入IoC容器~容器的适配器

回到目录 关于依赖倒置(DIP) 高层模块不依赖于低层模块的实现,而低层模块依赖于高层模块定义的接口,通俗的讲,就是高层模块定义接口,低层模块负责实现,这在我们实际开发中经常被用到,层与层之间引用,经常被添加一个接口层去隔离,在接口层定义相关业务规范,而底层去实现它,高层只引用这个接口,当高级需要其它扩展,直接添加新的接口,由新的底层模块去实现即可,底层其它代码不需要修改,这也完全复合开闭原则(OCP). 关于控制反转(IOC) 控制反转是一种设计模式,像单例,工厂,适合器都属于设计模式的一种,

Lind.DDD.ILogicDeleteBehavor~逻辑删除的实现

回到目录 关于逻辑删除 对于逻辑删除之前的做法是在实体类中加个字段,一般是status,其中一种状态是删除,当然也有其它做法,如加个bool的字段IsDeleted,这些其实都过于武断,即它在基类里加上后,所以实体类都会有这种特性,而对于现实的数据表,可能不显示这种逻辑删除的特性,如关系表,日志表,可能删除就是物理上的直接delete,而这种删除字段加上去,我们的做也是在业务层手动调用update方法,或者在底层提供一个delete方法的重载,总之,感觉不是很爽! 看了ABP的软删除之后,对大叔

C#中方法,属性与索引器

C#中方法,属性与索引器: TODO: 1,关于系统中经常出现的通过某一字段,查询对应实体信息(可能是一条字段或一条数据和一组泛型集合) 讲解篇:1,方法,2,属性3,索引器 1,方法(1,根据状态编码返回状态名称:一条字段2,根据状态返回一条数据:实体) 1,根据状态编码返回状态名称:一条字段 /// <summary> /// 根据状态返回状态名称 /// </summary> /// <param name="value"></param

Lind.DDD.ConfigConstants统一管理系统配置

仓储大叔好了相赠 网上2500元 跟谁学课堂 C#视频 ddd领域驱动架构设计视频 还赠送ABP视频两套 qq 2589406800 qq1399494644 qq2128543647 qq2890083872 qq3235634116 qq3381945576 qq2171713479源代码框架lind.ddd(后台管理系统,电商系统,API,SSO,xamarin,ko,各组件单元测试) 赠送ABP视频+lindCore源碼+LindAgility(敏捷框架) (60元) Lind.DDD.