1. 程式人生 > >Lind.DDD~實體屬性變更追蹤器的實現

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;

回到目錄