在架构设计中,利用领域驱动开发时,涉及到do(领域对象)和dto(数据传输对象)的相互装换匹配,这段代码简单但是重复频率太多,写得我很冒火(我有个职责是wcf SOA包装),我是个不喜欢重复劳动的懒人,我在网上搜索等到很多实体匹配的框架EmitMapper,AutoMapper等,但是他们都不能满足dto和do的对象的按规则匹配包装。最后我只得花了半个小时写了一个简单的代码生成器,完成了我的任务。但是事后总觉得不爽,于是有了写下这个AgileMapper框架来适应领域开发中的po,do,dto,vo着一些列对象的相互包装,建立一个按规则包装的Mapper框架。项目已经完成上传于CodePlex http://agilemapper.codeplex.com/ ,目前刚成型,希望大家能够帮助测试,提出bug,或者修复。我不是很清楚开源协议,选择了一个 协议。大家可以随便使用和修改应用来满足各自的需求,但是如果有些bug修复或者好的通用的修改希望大家能够,提交供我和其他人学习共同进步,但是这不是必须的,你也可以选择保留。

       AgileMapper架构设计类图:

AgileMapper

    在AgileMapper中支持多种MappingConfigurator(匹配管理器)都集成至MappingConfiguratorBase(MappingConfiguratorBase中拥有唯一的对象之间对于相等的默认表达式守信,针对于dto转化为do对象级联删除情况),内置了AttributeMappingConfigurator,XMLMappingConfigurator,DataRowMappingConfigurator三种匹配管理器。支持xml书写,attribute标记规则。由这些管理器根据具体标记标记方式产生一组IMappingRule(匹配规则),内置了5中匹配规则(简单,集合,表达式,对象,datarow)。

   在AgileMapper为我们提供了MappingConfiguratorBase的扩展,IMappingRule的扩展,已经多余Attribute标注的扩展CustomerMappingAttribute,已经xml的配置扩展。

下面我们来使用AgileMapper提供的内置Mapper。

测试预备:

  1. Domain Object:  
  2.  
  3. View Code   
  4. public class StudenDo    
  5.    {    
  6.        public int ID    
  7.        {    
  8.            get;    
  9.            set;    
  10.        }    
  11.  
  12.        public string Name    
  13.        { getset; }    
  14.  
  15.        public Sex Sex    
  16.        { getset; }    
  17.  
  18.        public Address Address    
  19.        { getset; }    
  20.  
  21.        public ContactWay ContactWay    
  22.        { getset; }    
  23.  
  24.        public List<string> CourseIds    
  25.        { getset; }    
  26.  
  27.        public List<KeyValuePair> Propertys    
  28.        { getset; }    
  29.  
  30.    }    
  31.  
  32.    public class KeyValuePair    
  33.    {    
  34.        public string Key    
  35.        { getset; }    
  36.  
  37.        public string Value    
  38.        { getset; }    
  39.    }    
  40.  
  41.    public enum Sex    
  42.    {    
  43.        男, 女    
  44.    }   
  45.  
  46.     
  47.  
  48. public class ContactWay    
  49.    {    
  50.       public string Phone    
  51.       {    
  52.           get;    
  53.           set;    
  54.       }    
  55.  
  56.       public string Email    
  57.       {    
  58.           get;    
  59.           set;    
  60.       }    
  61.  
  62.       public string QQ    
  63.       {    
  64.           get;    
  65.           set;    
  66.       }    
  67.    }   
  68.  
  69.     
  70.  
  71.     
  72.  
  73. public class Address    
  74.    {    
  75.        public string Country    
  76.        {    
  77.            get;    
  78.            set;    
  79.        }    
  80.  
  81.        public string Province    
  82.        { getset; }    
  83.  
  84.        public string Street    
  85.        { getset; }    
  86.  
  87.        public string Particular    
  88.        { getset; }    
  89.    }   
  90.  
  91.    
  92. Dto:  
  93.  
  94. View Code   
  95.      
  96.  
  97. public class StudenDto    
  98.    {    
  99.        public int ID    
  100.        {    
  101.            get;    
  102.            set;    
  103.        }    
  104.  
  105.        public string Name    
  106.        { getset; }    
  107.  
  108.        public Sex Sex    
  109.        { getset; }    
  110.  
  111.        [Mapping("Address.Country")]    
  112.        public string Country    
  113.        {    
  114.            get;    
  115.            set;    
  116.        }    
  117.  
  118.        [Mapping("Address.Province")]    
  119.        public string Province    
  120.        { getset; }    
  121.  
  122.        // [Mapping("Address.Street")]   
  123.        [IgnoreMapping]    
  124.        public string Street    
  125.        { getset; }    
  126.  
  127.        [ExpressionMapping("Address.Country +\" 国籍 \"+Address.Province +\" 省 \"")]    
  128.        public string Particular    
  129.        { getset; }    
  130.  
  131.        [ObjectMappingAttribute]    
  132.        public ContactWayDto ContactWay    
  133.        { getset; }    
  134.  
  135.        [CollectionMapping()]    
  136.        public List<string> CourseIds    
  137.        { getset; }    
  138.  
  139.        [CollectionMapping(EqualExpression="from.Key==to.Key",IsDeleteNotInFromItem=true)]    
  140.        public List<KeyValuePair> Propertys    
  141.        { getset; }    
  142.  
  143.        [ExpressionMapping("Propertys[0].Key")]    
  144.        public string FirstPropertyKey    
  145.        {    
  146.            get;    
  147.            set;    
  148.        }    
  149.  
  150.     
  151.  
  152. public class ContactWayDto    
  153.     {    
  154.         public string Phone    
  155.         {    
  156.             get;    
  157.             set;    
  158.         }    
  159.  
  160.         public string Email    
  161.         {    
  162.             get;    
  163.             set;    
  164.         }    
  165.  
  166.         public string QQ    
  167.         {    
  168.             get;    
  169.             set;    
  170.         }    
  171.     }   
  172.  
  173. public class AddressDto    
  174. {    
  175.     public string Country    
  176.     {    
  177.         get;    
  178.         set;    
  179.     }    
  180.  
  181.     public string Province    
  182.     { getset; }    
  183.  
  184.     public string Street    
  185.     { getset; }    
  186.  
  187.     public string Particular    
  188.     { getset; }    
  189. }   
  190.  

 

一:Attribute标注:

  1.  
  2.  
  3. View Code   
  4.      
  5.  
  6. [TestMethod]    
  7.        public void AttributeConfig_SimpleMapping_Gen()    
  8.        {    
  9.  
  10.            StudenDo stu = new StudenDo()    
  11.            {    
  12.                ID = 1,    
  13.                Name = "test1",    
  14.                Sex = Sex.女,    
  15.                Address = new Address()    
  16.                {    
  17.                    Country = "中国",    
  18.                    Province = "四川",    
  19.                    Street = "高新区"    
  20.                },    
  21.                CourseIds = new List<string>() { "1""2""3" },    
  22.                Propertys = new List<KeyValuePair>() { new KeyValuePair() { Key = "1", Value = "1" } },    
  23.                ContactWay = new ContactWay()    
  24.                {    
  25.                    Phone = "1111111111111111",    
  26.                    Email = "xxxx@12f",    
  27.                    QQ = "7889789999889"    
  28.                }    
  29.            };    
  30.  
  31.            var mapper = ObjectMapperManager.Default.GetMapper<StudenDto, StudenDo>();    
  32.  
  33.            var dt1 = DateTime.Now;    
  34.            var stuDto = mapper.Warp(stu);    
  35.            var sp = DateTime.Now - dt1;    
  36.  
  37.            dt1 = DateTime.Now;    
  38.            stuDto = mapper.Warp(stu);    
  39.            var sp1 = DateTime.Now - dt1;    
  40.  
  41.            Assert.AreEqual(stuDto.ID, stu.ID);    
  42.            Assert.AreEqual(stuDto.Name, stu.Name);    
  43.            Assert.AreEqual(stuDto.Sex, stu.Sex);    
  44.            Assert.AreEqual(stuDto.Country, stu.Address.Country);    
  45.            Assert.AreEqual(stuDto.Province, stu.Address.Province);    
  46.            Assert.AreEqual(stuDto.Street, null);//Ignore    
  47.            //object    
  48.            // Assert.AreEqual(stuDto.ContactWay,null);   
  49.            Assert.AreEqual(stuDto.ContactWay.QQ, stu.ContactWay.QQ);    
  50.            Assert.AreEqual(stuDto.ContactWay.Email, stu.ContactWay.Email);    
  51.            //expression   
  52.            Assert.AreEqual(stuDto.Particular, string.Format("{0} 国籍 {1} 省 ", stu.Address.Country, stu.Address.Province));    
  53.            Assert.AreEqual(stuDto.FirstPropertyKey, stu.Propertys[0].Key);    
  54.            //collection              
  55.            Assert.AreEqual(stuDto.CourseIds[0], stu.CourseIds[0]);    
  56.            Assert.AreEqual(stuDto.CourseIds.Count, stu.CourseIds.Count);    
  57.  
  58.            Assert.AreEqual(stuDto.Propertys[0].Key, stu.Propertys[0].Key);    
  59.            Assert.AreEqual(stuDto.Propertys[0].Value, stu.Propertys[0].Value);    
  60.            Assert.AreEqual(stuDto.Propertys.Count, stu.Propertys.Count);    
  61.  
  62.            //Warp 2   
  63.            var stuDo = new StudenDo();    
  64.            mapper.Warp(stuDto, stuDo);    
  65.  
  66.            Assert.AreEqual(stuDo.ID, stuDto.ID);    
  67.            Assert.AreEqual(stuDo.Name, stuDto.Name);    
  68.            Assert.AreEqual(stuDo.Sex, stuDto.Sex);    
  69.            Assert.AreEqual(stuDo.Address.Country, stuDto.Country);    
  70.            Assert.AreEqual(stuDo.Address.Province, stuDto.Province);    
  71.            //Assert.AreEqual(stuDo.Address.Street, null);//Ignore    
  72.            //object   
  73.            Assert.AreEqual(stuDo.ContactWay.QQ, stuDto.ContactWay.QQ);    
  74.            Assert.AreEqual(stuDo.ContactWay.Email, stuDto.ContactWay.Email);    
  75.            //collection   
  76.  
  77.            Assert.AreEqual(stuDo.CourseIds.Count, stuDto.CourseIds.Count);    
  78.            Assert.AreEqual(stuDo.CourseIds[0], stuDto.CourseIds[0]);    
  79.  
  80.            Assert.AreEqual(stuDo.Propertys.Count, stuDto.Propertys.Count);    
  81.            Assert.AreEqual(stuDo.Propertys[0].Key, stuDto.Propertys[0].Key);    
  82.            Assert.AreEqual(stuDo.Propertys[0].Value, stuDto.Propertys[0].Value);    
  83.        }    
  84.  
  85.        [TestMethod]    
  86.        public void AttributeConfig_SimpleMapping()    
  87.        {    
  88.            StudenDo stu = new StudenDo()    
  89.            {    
  90.                ID = 1,    
  91.                Name = "test1",    
  92.                Sex = Sex.女,    
  93.                Address = new Address()    
  94.                {    
  95.                    Country = "中国",    
  96.                    Province = "四川",    
  97.                    Street = "高新区"    
  98.                },    
  99.                CourseIds = new List<string>() { "1""2""3" },    
  100.                Propertys = new List<KeyValuePair>() { new KeyValuePair() { Key = "1", Value = "1" } },    
  101.                ContactWay = new ContactWay()    
  102.                {    
  103.                    Phone = "1111111111111111",    
  104.                    Email = "xxxx@12f",    
  105.                    QQ = "7889789999889"    
  106.                }    
  107.            };    
  108.  
  109.            var mapper = ObjectMapperManager.Default.GetMapper();    
  110.            var stuDto = mapper.Warp(typeof(StudenDto), stu) as StudenDto;    
  111.  
  112.            Assert.AreEqual(stuDto.ID, stu.ID);    
  113.            Assert.AreEqual(stuDto.Name, stu.Name);    
  114.            Assert.AreEqual(stuDto.Sex, stu.Sex);    
  115.            Assert.AreEqual(stuDto.Country, stu.Address.Country);    
  116.            Assert.AreEqual(stuDto.Province, stu.Address.Province);    
  117.            Assert.AreEqual(stuDto.Street, null);//Ignore    
  118.            //object   
  119.            Assert.AreEqual(stuDto.ContactWay.QQ, stu.ContactWay.QQ);    
  120.            Assert.AreEqual(stuDto.ContactWay.Email, stu.ContactWay.Email);    
  121.            //expression   
  122.            Assert.AreEqual(stuDto.Particular, string.Format("{0} 国籍 {1} 省 ", stu.Address.Country, stu.Address.Province));    
  123.            //collection              
  124.            Assert.AreEqual(stuDto.CourseIds[0], stu.CourseIds[0]);    
  125.            Assert.AreEqual(stuDto.CourseIds.Count, stu.CourseIds.Count);    
  126.        }   

二:xml配置标注规则:

  1. View Code   
  2. <?xml version="1.0" encoding="utf-8" ?>    
  3. <AgileMapper>    
  4.   <Extensions>    
  5.     <Extension Name="SimpleMappingRule" Type="Green.AgileMapper.SimpleMappingRule,Green.AgileMapper"></Extension>    
  6.     <Extension Name="ObjectMappingRule" Type="Green.AgileMapper.ObjectMappingRule,Green.AgileMapper"></Extension>    
  7.     <Extension Name="CollectionMappingRule" Type="Green.AgileMapper.CollectionMappingRule,Green.AgileMapper"></Extension>    
  8.     <Extension Name="ExpressionMappingRule" Type="Green.AgileMapper.ExpressionMappingRule,Green.AgileMapper"></Extension>    
  9.   </Extensions>    
  10.   <Mappings>    
  11.     <Mapping FromType="AgileMapper.Test.StudenDto,AgileMapper.Test"  >    
  12.       <SimpleMappingRule FromPoperty="Country" ToPoperty="Address.Country"></SimpleMappingRule>    
  13.       <SimpleMappingRule FromPoperty="Province" ToPoperty="Address.Province"></SimpleMappingRule>              
  14.       <ObjectMappingRule  FromPoperty="ContactWay" ToPoperty="ContactWay"></ObjectMappingRule>    
  15.       <CollectionMappingRule FromPoperty="CourseIds" ToPoperty="CourseIds"></CollectionMappingRule>    
  16.       <CollectionMappingRule FromPoperty="Propertys" ToPoperty="Propertys" EqualExpression="from.Key==to.Key" IsDeleteNotInFromItem="true"></CollectionMappingRule>    
  17.       <ExpressionMappingRule  FromPoperty="Particular" Expression="Address.Country +Address.Province"></ExpressionMappingRule>    
  18.       <ExpressionMappingRule  FromPoperty="FirstPropertyKey" Expression="Propertys[0].Key"></ExpressionMappingRule>    
  19.       <Ignores>    
  20.         <Ignore Name="Street"></Ignore>    
  21.       </Ignores>    
  22.     </Mapping>    
  23.   </Mappings>    
  24. </AgileMapper> 

测试代码:  

 

  1. [TestMethod]    
  2.         public void XMlConfig_SimpleMapping_Gen()    
  3.         {    
  4.  
  5.             StudenDo stu = new StudenDo()    
  6.             {    
  7.                 ID = 1,    
  8.                 Name = "test1",    
  9.                 Sex = Sex.女,    
  10.                 Address = new Address()    
  11.                 {    
  12.                     Country = "中国",    
  13.                     Province = "四川",    
  14.                     Street = "高新区"    
  15.                 },    
  16.                 CourseIds = new List<string>() { "1""2""3" },    
  17.                 Propertys = new List<KeyValuePair>() { new KeyValuePair() { Key = "1", Value = "1" } },    
  18.                 ContactWay = new ContactWay()    
  19.                 {    
  20.                     Phone = "1111111111111111",    
  21.                     Email = "xxxx@12f",    
  22.                     QQ = "7889789999889"    
  23.                 }    
  24.             };    
  25.  
  26.             var mapper = ObjectMapperManager.Default.GetMapper<StudenDto, StudenDo>(new XMLMappingConfigurator(@"E:\Project\OpenSource\AgileMapper\AgileMappper.Test\XMLConfigurator\AgileMapper.xml"));    
  27.  
  28.             var stuDto = mapper.Warp(stu);              
  29.  
  30.             Assert.AreEqual(stuDto.ID, stu.ID);    
  31.             Assert.AreEqual(stuDto.Name, stu.Name);    
  32.             Assert.AreEqual(stuDto.Sex, stu.Sex);    
  33.             Assert.AreEqual(stuDto.Country, stu.Address.Country);    
  34.             Assert.AreEqual(stuDto.Province, stu.Address.Province);    
  35.             Assert.AreEqual(stuDto.Street, null);//Ignore    
  36.             //object    
  37.             // Assert.AreEqual(stuDto.ContactWay,null);   
  38.             Assert.AreEqual(stuDto.ContactWay.QQ, stu.ContactWay.QQ);    
  39.             Assert.AreEqual(stuDto.ContactWay.Email, stu.ContactWay.Email);    
  40.             //expression   
  41.             Assert.AreEqual(stuDto.Particular.Replace(" """), string.Format("{0}{1}", stu.Address.Country, stu.Address.Province));    
  42.             Assert.AreEqual(stuDto.FirstPropertyKey, stu.Propertys[0].Key);    
  43.             //collection              
  44.             Assert.AreEqual(stuDto.CourseIds[0], stu.CourseIds[0]);    
  45.             Assert.AreEqual(stuDto.CourseIds.Count, stu.CourseIds.Count);    
  46.  
  47.             Assert.AreEqual(stuDto.Propertys[0].Key, stu.Propertys[0].Key);    
  48.             Assert.AreEqual(stuDto.Propertys[0].Value, stu.Propertys[0].Value);    
  49.             Assert.AreEqual(stuDto.Propertys.Count, stu.Propertys.Count);    
  50.  
  51.             //Warp 2   
  52.             var stuDo = new StudenDo();    
  53.             mapper.Warp(stuDto, stuDo);    
  54.  
  55.             Assert.AreEqual(stuDo.ID, stuDto.ID);    
  56.             Assert.AreEqual(stuDo.Name, stuDto.Name);    
  57.             Assert.AreEqual(stuDo.Sex, stuDto.Sex);    
  58.             Assert.AreEqual(stuDo.Address.Country, stuDto.Country);    
  59.             Assert.AreEqual(stuDo.Address.Province, stuDto.Province);    
  60.             //Assert.AreEqual(stuDo.Address.Street, null);//Ignore    
  61.             //object   
  62.             Assert.AreEqual(stuDo.ContactWay.QQ, stuDto.ContactWay.QQ);    
  63.             Assert.AreEqual(stuDo.ContactWay.Email, stuDto.ContactWay.Email);    
  64.             //collection   
  65.  
  66.             Assert.AreEqual(stuDo.CourseIds.Count, stuDto.CourseIds.Count);    
  67.             Assert.AreEqual(stuDo.CourseIds[0], stuDto.CourseIds[0]);    
  68.  
  69.             Assert.AreEqual(stuDo.Propertys.Count, stuDto.Propertys.Count);    
  70.             Assert.AreEqual(stuDo.Propertys[0].Key, stuDto.Propertys[0].Key);    
  71.             Assert.AreEqual(stuDo.Propertys[0].Value, stuDto.Propertys[0].Value);    
  72.         }  

三:DataRow的测试: 

测试预备StudentModelForDataRow:

  1. public class StudentModelForDataRow    
  2.     {    
  3.         public int ID    
  4.         { getset; }    
  5.  
  6.         public string Name    
  7.         { getset; }    
  8.     } 

测试代码:

  1. [TestMethod]    
  2.        public void DataRowConfig_SameTable_DataRowCloneMapping()    
  3.        {    
  4.            DataTable dt = new DataTable();    
  5.            dt.Columns.AddRange(new DataColumn[] {    
  6.                new DataColumn("ID",typeof(int)),    
  7.                new DataColumn("Name",typeof(string))    
  8.            });    
  9.  
  10.            var row = dt.NewRow();    
  11.            row[0] = 1;    
  12.            row[1] = "Green";    
  13.            dt.Rows.Add(row);    
  14.  
  15.            var rowClone = dt.NewRow();    
  16.  
  17.            var mapper = ObjectMapperManager.Default.GetMapper(new DataRowMappingConfigurator());    
  18.            mapper.Warp(typeof(DataRow), row, rowClone);    
  19.            Assert.AreEqual(row[0], rowClone[0]);    
  20.            Assert.AreEqual(row[1], rowClone[1]);    
  21.        }    
  22.  
  23.        [TestMethod]    
  24.        public void DataRowConfig_UnSameTable_MutipleRule_DataRowCloneMapping()    
  25.        {    
  26.            DataTable dt = new DataTable();    
  27.            dt.Columns.AddRange(new DataColumn[] {    
  28.                new DataColumn("ID",typeof(int)),    
  29.                new DataColumn("Name",typeof(string))    
  30.            });    
  31.  
  32.            DataTable dt2 = new DataTable();    
  33.            dt2.Columns.AddRange(new DataColumn[] {    
  34.                new DataColumn("ID",typeof(int)),    
  35.                new DataColumn("Name",typeof(string)),    
  36.                 new DataColumn("Sex",typeof(string))    
  37.            });    
  38.  
  39.            var row = dt2.NewRow();    
  40.            row[0] = 1;    
  41.            row[1] = "Green";    
  42.            row[2] = "Nan";    
  43.            dt2.Rows.Add(row);    
  44.  
  45.            var rowClone = dt.NewRow();    
  46.  
  47.            var mapper = ObjectMapperManager.Default.GetMapper(new DataRowMappingConfigurator());    
  48.            mapper.Warp(row, rowClone);    
  49.            Assert.AreEqual(row[0], rowClone[0]);    
  50.            Assert.AreEqual(row[1], rowClone[1]);    
  51.        }    
  52.  
  53.        [TestMethod]    
  54.        public void DataRowConfig_UnSameTable_Not_MutipleRule_DataRowCloneMapping()    
  55.        {    
  56.            DataTable dt = new DataTable();    
  57.            dt.Columns.AddRange(new DataColumn[] {    
  58.                new DataColumn("ID",typeof(int)),    
  59.                new DataColumn("Name",typeof(string))    
  60.            });    
  61.  
  62.            DataTable dt2 = new DataTable();    
  63.            dt2.Columns.AddRange(new DataColumn[] {    
  64.                new DataColumn("ID",typeof(int)),    
  65.                new DataColumn("Name",typeof(string)),    
  66.                 new DataColumn("Sex",typeof(string))    
  67.            });    
  68.  
  69.            var row = dt.NewRow();    
  70.            row[0] = 1;    
  71.            row[1] = "Green";    
  72.            dt.Rows.Add(row);    
  73.  
  74.            var rowClone = dt2.NewRow();    
  75.  
  76.            var mapper = ObjectMapperManager.Default.GetMapper(new DataRowMappingConfigurator());    
  77.            mapper.Warp(row, rowClone);    
  78.            Assert.AreEqual(row[0], rowClone[0]);    
  79.            Assert.AreEqual(row[1], rowClone[1]);    
  80.        }    
  81.  
  82.        [TestMethod]    
  83.        public void DataRowConfig_To_Object_CloneMapping()    
  84.        {    
  85.            DataTable dt = new DataTable();    
  86.            dt.Columns.AddRange(new DataColumn[] {    
  87.                new DataColumn("ID",typeof(int)),    
  88.                new DataColumn("Name",typeof(string))    
  89.            });    
  90.  
  91.            var row = dt.NewRow();    
  92.            row[0] = 1;    
  93.            row[1] = "Green";    
  94.            dt.Rows.Add(row);    
  95.            StudentModelForDataRow model = new StudentModelForDataRow();    
  96.  
  97.            var mapper = ObjectMapperManager.Default.GetMapper(new DataRowMappingConfigurator());    
  98.            mapper.Warp(row, model);    
  99.            Assert.AreEqual(model.ID, row[0]);    
  100.            Assert.AreEqual(model.Name, row[1]);    
  101.        }  
     DataRow匹配针对相同的表结构和不同表结构,以及实体类和DataRow之间的转化。 

单元测试结果:

QQ截图未命名

     对于xml配置的架构还没做,以及基于T4模板的按照规则代码生成模板还在进一步开发中,敬请期待。

     今天就写在这里了,欢迎大家的指正和修改,希望你的修改如果更好能通知我,给我好的建议和探讨,谢谢。