AutoMapper

    在开发中,为了减少系统负载,一般不会吧整个表的字段(即Model对象所有属性)作为传输的数据,而是单独根据具体业务需求,写一些新的类(一般以DTO结尾命名)只包含整个表字段的某些字段,用来进行数据的传输,这些类的对象称为DTO对象(数据传输对象),DTO对象与数据库实体对象Mdoel之间的赋值过程可以通过AutoMapper来实现,从而减少代码量。AutoMapper是.NET的一个对象映射工具,用于数据对象传输DTO,实现DTO对象和实体对象之间的赋值转换。引入AutoMapper.dll.

    1.定义映射规则:

AutoMapper是基于约定的,在映射之前需要进行映射规则的配置:

   public class Source
    {
        public int SomeValue { get; set; }
        public string AnotherValue { get; set; }
    }

<pre name="code" class="csharp">   public class SourceDTO
    {
        public int SomeValue { get; set; }
    }

 映射规则: 

Mapper.CreateMap<Source, SourceDTO>();

简易映射测试:

    将Source类的对象映射到SourceDTO类的对象上

Source src = new Source() { SomeValue = 1, AnotherValue = "2" };

SourceDTO dest = Mapper.Map<SourceDTO>(src);

Console.WriteLine(dest.SomeValue);//1

2.通过AutoMapper.Profile中的CreateMap来配置映射规则:

    Profile提供了一个命名的映射类,所有继承自Profile类的子类都是一个映射集合。为每个类定义的Profile类都要继承自Profile.在其Profile类中重写Porfile类的Configure方法,并在该方法中定义该类与DTO类之间转换的映射规则:如SourceProfile.cs:

        protected override void Configure()
        {
            //1.0 添加第一个映射规则,Source和Destination的映射
            CreateMap<Source, SourceDTO>();

            //2.0 添加第二个映射规则
            //同名的会自动映射
            //这里添加Source类的AnotherValue属性和Source2DTO的AnotherValue2属性进行映射
            CreateMap<Source, Source2DTO>().ForMember(d => d.AnotherValue2,
                opt => 
                { 
                    opt.MapFrom(s => s.AnotherValue);
            });
        }

然后在global.asax的Application_Start方法中初始化Mapper规则:

Mapper.Initialize(a => a.AddProfile<SourceProfile>());
 3. 定义Configuration.cs类:

     由于在一个项目中可能会有很多DTO类,对应的Profile类也很多,为了方便管理这些Profile类,可以定义一个Configuration.cs初始化所有的Profile,在Global.asax的Application_Start()中调用一次就可以完成所有实体与DTO之间的映射规则的配置:

public class Configuration
{
    public static void Configure()
    {
        Mapper.Initialize(cfg =>
        {
            cfg.AddProfile<Profiles.SourceProfile>();
            cfg.AddProfile<Profiles.OrderProfile>();
            cfg.AddProfile<Profiles.CalendarEventProfile>();
        });
    }
}
在global.asax的Application_Start()中调用:

AutoMapper.Configuration.Configure();
在项目目录结构中Profile目录结构可以这样:



4.扁平化映射(Flattening):

    默认情况下,我们的Source类和SourceDTO类是根据属性名称进行匹配映射的。除此之外,默认的映射规则还有下面两种情况,我们称之为扁平化映射,即当Source类中不包含SourceDTO类中的属性的时候,AutoMapper会将SourceDTO类中的属性进行分割,或匹配Source中“Get”开头的方法,例如:

public class Order
{
    public Customer Customer { get; set; }

    public decimal GetTotal()
    {
        return 100M;
    }
}
public class OrderDto
{
    public string CustomerName { get; set; }
    public string Total { get; set; }
}
public class Customer
{
    public string Name { get; set; }
}

我们在进行映射的时候,不需要进行特殊的配置,既可以完成从Order到OrderDto的映射:

public class OrderProfile : Profile
{
    protected override void Configure()
    {
        CreateMap<Entity.Order, Dto.OrderDto>();
    }
}
测试:

Entity.Customer customer = new Entity.Customer() { Name = "Tom" };
Entity.Order order = new Entity.Order() { Customer = customer };
Dto.OrderDto orderDto = Mapper.Map<Dto.OrderDto>(order);
Console.WriteLine(orderDto.Total);//100

5.手动指定映射字段:

    在实际的业务环境中,我们的Source类和Destination类的字段不可能一对一的匹配,这个时候我们就需要来指定他们的实际映射关系,例如:

public class CalendarEvent
{
    public DateTime Date { get; set; }
    public string Title { get; set; }
}

public class CalendarEventDTO
{
    public DateTime EventDate { get; set; }
    public int EventHour { get; set; }
    public int EventMinute { get; set; }
    public string DisplayTitle { get; set; }
}
在这两个类中,CalendarEvent的Date将被拆分为CalendarEventForm的日期、时、分三个字段,Title也将对应DisplayTitle字段,那么相应的Profile定义如下:
public class CalendarEventProfile : Profile
{
    protected override void Configure()
    {
        CreateMap<Entity.CalendarEvent, Entity.CalendarEventDTO>()
            .ForMember(dest => dest.EventDate, opt => opt.MapFrom(src => src.Date.Date))
            .ForMember(dest => dest.EventHour, opt => opt.MapFrom(src => src.Date.Hour))
            .ForMember(dest => dest.EventMinute, opt => opt.MapFrom(src => src.Date.Minute))
            .ForMember(dest => dest.DisplayTitle, opt => opt.MapFrom(src => src.Title));
    }
}

测试:

Entity.CalendarEvent calendarEvent = new Entity.CalendarEvent()
{
    Date = DateTime.Now,
    Title = "Demo Event"
};
Entity.CalendarEventForm calendarEventDTO = Mapper.Map<Entity.CalendarEventDTO>(calendarEvent);
Console.WriteLine(calendarEventDTO.EventDate)//2016/07/08

6.验证配置项(Configuration Validation):

AutoMapper提供了一种验证机制,用来判断DTO类中的所有属性是否都被映射,如果存在未被映射的属性,则抛出异常。

<pre name="code" class="csharp">public class Source
{
    public int SomeValue { get; set; }
    public string AnotherValue { get; set; }
}

 
public class SourceDTO
{
    public int SomeValuefff { get; set; }
}
测试:

Mapper.CreateMap<Entity.Source, Entity.Destination>();
Mapper.AssertConfigurationIsValid();
此时抛出异常AutoMapperConfigurationException,这是因为SourceDTO中的SomeValuefff在Source类中没有对应的字段造成的,解决方法:

指定映射字段:

Mapper.CreateMap<Entity.Source, Entity.SourceDTO>()
    .ForMember(dest => dest.SomeValuefff, opt =>
    {
        opt.MapFrom(src => src.SomeValue);
    });
或使用Ignore方法:

Mapper.CreateMap<Entity.Source, Entity.SourceDTO>()
    .ForMember(dest => dest.SomeValuefff, opt =>
    {
        opt.Ignore();
    });
或使用自定义解析器,见下讲:


7.自定义解析器:(Custom value resolvers)

AutoMapper允许我们自定义解析器来完成Source到SourceDTO的值的转换:

public class Source
{
    public int Value1 { get; set; }
    public int Value2 { get; set; }
}

public class SourceDTO
{
    public int Total { get; set; }
}
SourceDTO中的Total属性在Source中不存在,如果现在创建映射规则,在映射的时候必然会抛出异常。这个时候我们就需要使用自定义解析器来完成映射。自定义解析器需要实现 IValueResolver 接口:

public class CustomResolver : ValueResolver<Source, int>
{
    protected override int ResolveCore(Source source)
    {
        return source.Value1 + source.Value2;
    }
}
然后在映射规则中使用这个解析器:
public class SourceProfile : Profile
{
    protected override void Configure()
    {
        //Source->Destination
        CreateMap<Source, SourceDTO>()
            .ForMember(dest => dest.Total, opt =>
            {
                opt.ResolveUsing<CustomResolver>();
            });
    }
}
测试:

Source src = new Source()
{
    Value1 = 1,
    Value2 = 2
};
SourceDTO dest = Mapper.Map<SourceDTO>(src);
Console.WriteLine(dest.Total);//3
在使用自定义Resolver中,我们还可以指定Resolver的构造函数,例如:

CreateMap<Source, SourceDTO>()
    .ForMember(dest => dest.Total, opt =>
    {
        opt.ResolveUsing<CustomResolver>()
            <span style="color:#FF0000;">.ConstructedBy(() => new CustomResolver());</span>
    });

8.自定义类型转换器:(Custom type converters)

AutoMapper通过ConvertUsing来使用自定义类型转换器。ConvertUsing有三种用法:

void ConvertUsing(Func<TSource, TDestination> mappingFunction);
void ConvertUsing(ITypeConverter<TSource, TDestination> converter);
void ConvertUsing<TTypeConverter>() where TTypeConverter : ITypeConverter<TSource, TDestination>;

举例:

public class Source
{
    public string Value1 { get; set; }
}

public class SourceDTO
{
    public int Value1 { get; set; }
}
使用如下配置:

public class SourceProfile : Profile
{
    protected override void Configure()
    {
        //string->int
        CreateMap<string, int>()
            .ConvertUsing(Convert.ToInt32);
        //Source->SourceDTO
        CreateMap<Source, SourceDTO>();
    }
}

在上面的配置中,我们首先创建了从string到int的类型转换,这里使用了系统自带的Convert.ToInt32转换方法。

除了这种方法之外,我们还可以自定义类型转换器:

public class CustomConverter : ITypeConverter<Source, SourceDTO>
{
    public SourceDTO Convert(ResolutionContext context)
    {
        Source src = context.SourceValue as Source;
        SourceDTO dest = new SourceDTO();
        dest.Value1 = System.Convert.ToInt32(src.Value1);

        return dest;
    }
}
通过这个转换器,我们可以绕过string到int的转换,直接将Source类的对象转换为Destination类的对象。

对应的配置如下:
public class SourceProfile : Profile
{
    protected override void Configure()
    {
        //Source->Destination
        CreateMap<Source, SourceDTO>()
            .ConvertUsing<CustomConverter>();
    }
}
或者,我们也可以使用下面的配置:

public class SourceProfile : Profile
{
    protected override void Configure()
    {
        //Source->Destination
        CustomConverter converter = new CustomConverter();
        CreateMap<Source, SourceDTO>()
            .ConvertUsing(converter);
    }
}

9.空值替换:(Null substitution)

空值替换允许我们将Source对象中的空值在转换为SourceDTO的值的时候,使用指定的值来替换空值。

public class Source
{
public string Value { get; set; }
}

public class SourceDTO
{
public string Value { get; set; }
}



public class SourceProfile : Profile
{
    protected override void Configure()
    {
        //Source->Destination
        CreateMap<Source, SourceDTO>()
            .ForMember(dest => dest.Value, opt =>
            {
                opt.NullSubstitute("原始值为NULL");
            });
    }
}
Source src = new Source();
Destination dest = Mapper.Map<Destination>(src);
Console.WriteLine(dest.Value);//原始值为Null

10.条件映射:(Conditional mapping)

条件映射只当Source类中的属性值满足一定条件的时候才进行映射。例如:

public class Foo
{
    public int baz;
}

public class FooDTO
{
    public uint baz;

Mapper.CreateMap<Foo, FooDTO>()
    .ForMember(dest => dest.baz, opt =>
    {
        opt.Condition(src => (src.baz >= 0));
    });




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值