创建自己的AutoMapper类,从此不用手动建立对象映射

AutoMapper-对象自动映射

在企业应用开发中,很多时候都需要将一个对象转换为另一个对象,比如说在WCF应用的开发中,需要将一个Entity转换为一个Contract对象。大部分情况下,这两个对象会非常相似,有个相同的属性名和类型。如果每次我们都要手写这中转换代码,不但容易出错,而且工作量也非常可观。所以开发一个自动映射类还是非常必要的。我们把此类命名为AutoMapper,它应该实现以下功能:

如果分属两个类的两个属性在满足某一类条件时可以映射:

  1. 集合映射,所有实现了IEnumerable<T>的类型都作为集合类映射。集合映射又包括以下两种映射:

    1. 基本集合映射,集合内元素类型相同。

    2. Twin集合映射,集合内元素类型不同,而且都为引用类型但不是string

  2. 单值映射,非集合映射的属性,使用单值映射。单值映射也包括以下两种映射。

    1. 基本类型映射,属性类型相同者,使用此映射。

    2. Twin映射,属性类型不同且都为引用类型但不是string类型,使用此类映射。

对于以上映射,映射成立,必须满足一下条件:

  1. 属性名相同

  2. 源属性必须可读。

  3. 如果目标属性是值类型或者string类型,那么目标属性必须可写。

  4. 如果目标属性是引用类型,那么目标属性可读,而且可以得到一个非空值,否则目标属性必须可写。

AutoMapper类型还应该提供配置功能,调用这可以为某个映射配置,配置包括忽略某个属性映射,即使该属性满足映射条件。将两个不同名的属性配置为可以映射,属性类型必须满足映射条件,否则运行时,该属性还是不能映射。关于映射配置,将在后面讲述。

AutoMapper类应该有如下方法定义:

public static class AutoMapper

{        

 

   /// <summary>

   /// 将一个集合转换为另一个集合,两种类型必须是可转换的.

   /// </summary>

   public static IEnumerable<TTarget>MapTo<TSource, TTarget>(this IEnumerable<TSource> sourceItems)

            where TSource : class

            where TTarget : class, new();

 

       /// <summary>

       /// 将源对象转换为目标类型的对象。

       /// </summary>   

   public static TTarget MapTo<TSource,TTarget>(this TSource source)

      where TSource : class

      where TTarget : class, new();

 

   /// <summary>

   /// 将源对象转换为目标类型的对象。目标对象需要调用者事先创建出来

   /// </summary>

   public static TTarget MapTo<TSource,TTarget>(this TSource source, TTarget target)

      where TSource : class

      where TTarget : class;

}

 

AutoMapper类依赖于TypeMap<TSource,TTarget>类型来映射指定了源和目标类型的映射。TypeMap<TSource,TTarget>类型将会根据两个类型来建立一个PropertyMap对象的集合,PropertyMap对象负责单个属性的映射。从PropertyMap的继承关系如下:

interface IpropertyMap

                abstract classPropertyMap : IpropertyMap

class BasicPropertyMap : PropertyMap

class TwinPropertyMap : PropertyMap

abstract class EnumerablePropertyMap :PropertyMap

                classBasicEnumerablePropertyMap : EnumerablePropertyMap

classTwinEnumerablePropertyMap : EnumerablePropertyMap

 

继承层次的叶子类跟前面讲述的集中映射类型一一对应。

为了提高映射性能,使用了OpenDelegate。关于开放委托,网上有详细介绍。

 

提供了MapConfiguration<TSource,TTarget>类来完成配置映射的任务。该类具有以下两个方法来完成映射配置。

public class MapConfiguration<TSource, TTarget> : MapConfiguration

       where TSource : class

       where TTarget : class

{

   public MapConfiguration<TSource, TTarget>Ignore<TProperty>(Expression<Func<TSource, TProperty>> propExp);       

 

   public MapConfiguration<TSource, TTarget>MapName<TProperty>(

       Expression<Func<TSource, TProperty>> sourcePropExp,

       Expression<Func<TTarget, TProperty>> targetPropExp);

 

   public bool IsIgnored(string propName);

 

   public bool TryGetMappedProperty(string sourceProp, out string targetProp);

}

 

下面是几个使用AutoMapper类的例子:

User u = new User()

{

   Bool = true,

   Byte = 1,

   Char = 'f',

   DateTime = DateTime.Now

};

 

var c = u.MapTo<User, UserContract>();

Assert.AreEqual(u.Bool, c.Bool);

Assert.AreEqual(u.Byte, c.Byte);

Assert.AreEqual(u.Char, c.Char);

Assert.AreEqual(u.DateTime,c.DateTime);

 

节省的代码是显而易见的,而且性能也不差。

下面是使用配置的例子,一定要在调用MapTo之前配置好映射关系,否则之后配置的映射关系是不起作用的。

 

[ClassInitialize()]

        public static void MyClassInitialize(TestContext testContext)

        {

             AutoMapper.Configure<User, UserContract>()

                .MapName(user =>user.AccountName, uc => uc.UserName)

                .Ignore(user => user.Byte);

        }

 

User u = new User()

{

   Bool = true,

   Byte = 1,              

};

 

var c = u.MapTo<User, UserContract>();

 

Assert.AreEqual(u.Byte, 1);

Assert.AreEqual(c.Byte, 0);    

为不同名属性指定映射:

 

User u = new User()

{

   AccountName = "mac"

};

 

var c = u.MapTo<User, UserContract>();

Assert.AreEqual(u.AccountName,c.UserName);

 

下面是代码部分:

该帮助类用来封装一下反射代码:

internal static class ReflectionUtils

{

//在运行时得到一个实现了IEnumerable<T>类型的泛型参数的类型。

       public static Type GetElementTypeOfIEnumerable(Type type)

       {

            if (type.IsArray)

            {

                return type.GetElementType();

            }

 

            if (type.IsInterface)

            {

                Type def =type.GetGenericTypeDefinition();

                if (def == typeof(IEnumerable<>))

                {

                    returntype.GetGenericArguments()[0];

                }

            }

            else

            {

                Type interfaceType = type

                    .GetInterfaces()

                    .FirstOrDefault(t =>t.IsGenericType && t.GetGenericTypeDefinition() == typeof(IEnumerable<>));

                if (interfaceType != null)

                {

                    returninterfaceType.GetGenericArguments()[0];

                }

            }

 

            return null;

       }

 

//判断一个类型是引用类型但不是string类型。

       public static bool IsReferenceTypeButString(Type type)

       {

            return !type.IsValueType &&type != typeof(string);

       }

//判断一个类型是值类型或者是string类型。

       public static bool IsValueTypeOrString(Type type)

       {

            return type.IsValueType || type == typeof(string);

       }

//创建一个集合,如果是数组,就会使用count参数创建指定长度的数组

       public static IEnumerable<object> CreateCollection(Type collType, int count)

       {

            object instance = null;

            var elementType = ReflectionUtils.GetElementTypeOfIEnumerable(collType);

           

            if (collType.IsArray)

            {                

                instance = Array.CreateInstance(elementType,count);               

            }

            else if (collType.IsInterface)

            {

                var listType = typeof(List<>).MakeGenericType(elementType);

 

                if(collType.IsAssignableFrom(listType))

                {

                    instance = Activator.CreateInstance(listType);

                }

            }

            else

            {

                instance =CreateInstance(collType);

            }

 

            return instance as IEnumerable<object>;

       }

//sources中的对象拷贝到目标集合中。Target必须是一个数组或者实现了Ilist接口。

       public static void CopyElements(object[] source, object target)

       {

            if(source =

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
AutoMapper是一个用于对象映射的开源库。它可以帮助简化对象之间的转换过程,减少手动编写重复的代码。通过配置映射规则,AutoMapper可以自动将一个对象的属性值复制到另一个对象中对应的属性上,而不需要手动逐个属性进行赋值。 使用AutoMapper,你可以定义映射规则,包括源型和目标型以及它们之间的属性映射关系。一旦配置好映射规则,你可以使用简单的API将源对象映射到目标对象上。 以下是一个使用AutoMapper的示例: ```csharp // 定义源型和目标型 public class SourceObject { public string Name { get; set; } public int Age { get; set; } } public class DestinationObject { public string Name { get; set; } public int Age { get; set; } } // 配置映射规则 var config = new MapperConfiguration(cfg => { cfg.CreateMap<SourceObject, DestinationObject>(); }); // 创建映射器 var mapper = config.CreateMapper(); // 创建对象 var source = new SourceObject { Name = "John", Age = 30 }; // 使用映射器进行对象映射 var destination = mapper.Map<SourceObject, DestinationObject>(source); // 输出目标对象属性值 Console.WriteLine(destination.Name); // 输出:John Console.WriteLine(destination.Age); // 输出:30 ``` 通过使用AutoMapper,你可以简化对象之间的映射过程,提高开发效率。它支持各种复杂的映射场景,并且可以通过自定义转换器来处理更复杂的映射逻辑。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值