2017年3月26日 星期日

[C#] 簡單自製 Auto Model Mapper

先撇除網路上有很多有關Mapper的套件或工具,簡單做一個Model Mapper的功能。




資料通常會使用ORM Mapper到 Model上,再將結果View Model將資料直接傳到View上作使用,當變更行為確認時會將View Model傳回Controller寫入資料到DB。

但是View Model也許是多個Entity Model所組成因此在沒有使用任何Mapper工具底下就必須寫類似下方的程式碼。

    
            User User = new User();
            Mail Mail = new Mail();
            Address Address = new Address();

            User.UserId = UserV.UserId;
            User.UserName = UserV.UserName;
            User.Sex = UserV.Sex;
            Mail.EMail = UserV.EMail;
            Address.Address1 = UserV.Address1;
            Address.Address2 = UserV.Address2;
            Address.Address3 = UserV.Address3;


為了解決這個問題因此網路上有一些可以做到Mapper的工具AutoMapperTinyMapperExpressMapper等等...,也有一些評比當然使用上是依照專案需求。


本次實作的概念:將UserV Mapper到User、Tel、Address、Mail簡易做法,Class Name作為Table Name,Attributes Name作為Column Name、透過這樣定義完成 Mapper。

使用方法:
屬性定義[CustomerAttributes.RefColumn("Class Name,Attributes Name;....")]
    
    [CustomerAttributes.RefColumn("User,UserId;Address,UserId;Mail,UserId;Tel,UserId")]
    public string UserId { get; set; }

使用範例:
    
        private void button1_Click(object sender, EventArgs e)
        {
            UserV UserV = new UserV();
            UserV.UserId = "E001";
            UserV.UserName = "Name";
            UserV.Sex = "M";
            UserV.EMail = "Test@Test.com.tw";
            UserV.Address1 = "台北市";

            User objUser = UserV.ToNewObject<User>(new User(), "User");

            Mail objMail = UserV.ToNewObject<Mail>(new Mail(), "Mail");
        }

Model:
User、Tel、Address、Mail
    
    public class User
    {
        public string UserId { get; set; }
        public string UserName { get; set; }
        public string Sex { get; set; }
    }

    public class Tel
    {
        public string UserId { get; set; }

        public string TelAreacode { get; set; }
        public string TelNo { get; set; }
        public string TelExt { get; set; }
        public string TelType { get; set; }
    }

    public class Address
    {
        public string UserId { get; set; }
        public string Address1 { get; set; }
        public string Address2 { get; set; }
        public string Address3 { get; set; }
    }

    public class Mail
    {
        public string UserId { get; set; }
        public string EMail { get; set; }
    }



    public class UserV
    {
        [CustomerAttributes.RefColumn("User,UserId;Address,UserId;Mail,UserId;Tel,UserId")]
        public string UserId { get; set; }
        [CustomerAttributes.RefColumn("User,UserName")]
        public string UserName { get; set; }
        [CustomerAttributes.RefColumn("User,Sex")]
        public string Sex { get; set; }
        [CustomerAttributes.RefColumn("Address,Address1")]
        public string Address1 { get; set; }
        [CustomerAttributes.RefColumn("Address,Address2")]
        public string Address2 { get; set; }
        [CustomerAttributes.RefColumn("Address,Address3")]
        public string Address3 { get; set; }
        [CustomerAttributes.RefColumn("Mail,EMail")]
        public string EMail { get; set; }
        [CustomerAttributes.RefColumn("Tel,TelAreacode")]
        public string TelAreacode { get; set; }
        [CustomerAttributes.RefColumn("Tel,TelNo")]
        public string TelNo { get; set; }
        [CustomerAttributes.RefColumn("Tel,TelExt")]
        public string TelExt { get; set; }
        [CustomerAttributes.RefColumn("Tel,TelType")]
        public string TelType { get; set; }
    }

結果:


程式碼:
新增CustomerAttributes.cs
定義Mapper需要屬性
    
[AttributeUsage(AttributeTargets.Property, Inherited = false)]
public class RefColumn : Attribute
{
    private string _obj;

    /// <summary>
    /// 設定關聯欄位(支援多筆,使用分號相隔,每筆資料[Table,Column];例:CNFLWF,CONT_NO_OR_MA_STOP_NO;CNCOVM,CONT_NO)
    /// </summary>
    /// <param name="obj" />關聯欄位字串格式
    public RefColumn(string obj)
    {
        this._obj = obj;
    }

    public class Receive
    {
         public IList<refcolumnobj> RefColumn { get; set; }
    }

    public class RefColumnObj
    {
        /// <summary>
        /// Table Name
        /// </summary>
        public string Table { get; set; }
        /// <summary>
        /// Column Name
        /// </summary>
        public string Column { get; set; }
    }
}

實作CustomerAttributes.RefColumn.GetValue
將傳入的定義解析(";" 幾組)("," [0]Class Name、[1]Attributes Name),找出該對應的Class屬性。
   
        public IList GetValue
            {
                get
                {
                    IList result = new List();
                    string[] cols = _obj.Split(';');

                    foreach (var item in cols)
                    {
                        string[] paras = item.Split(',');
                        result.Add(new RefColumnObj() { Table = paras[0].Trim(), Column = paras[1].Trim() });
                    }
                    return result;
                }
            }

實作CustomerAttributes.GetRefColumn
取得RefColumn的資料。
  
        /// <summary>
        /// 取得RefColumn的資料
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="property"></param>
        /// <returns></returns>
        public static IList<RefColumn.RefColumnObj> GetRefColumn(System.Reflection.PropertyInfo property)
        {
            var attrs = (RefColumn[])property.GetCustomAttributes(typeof(RefColumn), false);
            //IList<RefColumn.RefColumnObj> result = new List<RefColumn.RefColumnObj>();

            //foreach (var item in attrs)
            //{
                //result = item.GetValue;
            //}
            IList result = attrs.Select(x => x.GetValue).ToList()[0];
            return result;

        }


實作ToNewObject
將傳入的Class 轉換為指定輸出的Class。
   
        /// <summary>
        /// 複製成new object
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="source"></param>
        /// <param name="target"></param>
        /// <param name="tableName"></param>
        /// <returns></returns>
        public static T ToNewObject(this object source, T target, string tableName)
        {
            if (source != null)
            {
                foreach (PropertyInfo objItem in source.GetType().GetProperties())
                {
                    var attr = CustomerAttributes.GetRefColumn(objItem);

                    foreach (var item in attr)
                    {
                        if (item.Table.Equals(tableName))
                        {
                            PropertyInfo property = target.GetType().GetProperty(item.Column);

                            if (property != null && objItem.GetValue(source, null) != null && property.PropertyType == objItem.PropertyType)
                            {
                                //找出是否有對應欄位,有則設值
                                property.SetValue(target, objItem.GetValue(source, null), null);
                            }

                        }
                    }
                }
            }

            return target;
        }

參考:
專案經驗



沒有留言:

張貼留言