.NET Core中使用AutoMapper

何为AutoMapper

AutoMapper是对象到对象的映射工具。在完成映射规则之后,AutoMapper可以将源对象转换为目标对象。

安装AutoMapper

这里我们在NuGet中下载安装AutoMapper。如下图

在项目中执行此命令

安装完成后 即可查看到依赖项中的 NuGet

配置AutoMapper映射规则

AutoMapper是基于约定的,因此在实用映射之前,我们需要先进行映射规则的配置。

public class ClubDetail
{
        public int Id { get; set; }

        public string Name { get; set; }

        public string Creator { get; set; }

        public int Year { get; set; }

        public string Contact { get; set; }

        public string Phone { get; set; }

        public string Address { get; set; }

        public string Description { get; set; }

        public byte[] ImageData { get; set; }

        public string CreateNumber { get; set; }
}

public class ClubDetailModel
{
        public int Id { get; set; }

        public string Name { get; set; }

        public string Creator { get; set; }

        public int Year { get; set; }

        public string Contact { get; set; }

        public string Phone { get; set; }

        public string Address { get; set; }

        public string Description { get; set; }

        public string ImageDataBase64 { get; set; }

        public byte[] ImageData { get; set; }

        public IList<IFormFile> ImageUpload { get; set; }

        public string CreateNumber { get; set; }

}

在上面的代码中,我们定义了两个类,我们需要将ClubDetil类的对象映射到ClubDetailModel类的对象上面。要完成这个操作,我们需要对AutoMapper进行如下配置:

CreateMap<ClubDetail, ClubDetailModel>();

用法:

这样,返回得到的就是映射后的ClubDetailModel了。

Profile的用法

Profile提供了一个命名的映射类,所有继承自Profile类的子类都是一个映射集合。

我们来看一下Profile的用法,这个例子中仍然使用上面的ClubDetil类和ClubDetailModel类。

public class ServiceProfiles : Profile
{
        public ServiceProfiles()
        {
            CreateMap<ClubDetail, ClubDetailModel>().ReverseMap();//加ReverseMap是让两个实体可以相互映射
} }

然后 我们在Startup中ConfigureServices配置和初始化

Mapper.Initialize(cfg =>
            {
                cfg.AddProfile<ServiceProfiles>();
            });
            services.AddAutoMapper();

在一个Profile中,我们可以完成多个、更复杂的规则的约定:

public class Destination2
{
    public int Id{ get; set; }
    public string Name{ get; set; }
}
public class ServiceProfiles : Profile
{
    public ServiceProfiles()
    {
       CreateMap<ClubDetail, ClubDetailModel>().ReverseMap();

    CreateMap<ClubDetail, Destination2>().ForMember(d => d.AnotherValue2, opt =>
       {
         opt.MapFrom(s => s.AnotherValue);
       });
   }
}

AutoMapper最佳实践

这段内容将讨论AutoMapper的规则写在什么地方的问题。

在上一段中,我们已经知道了如何使用AutoMapper进行简单的对象映射,但是,在实际的项目中,我们会有很多类进行映射(从Entity转换为Dto,或者从Entity转换为ViewModel等),这么多的映射如何组织将成为一个问题。

首先我们需要定义一个Configuration.cs的类,该类提供AutoMapper规则配置的入口,它只提供一个静态的方法,在程序第一次运行的时候调用该方法完成配置。

当有多个Profile的时候,我们可以这样添加:

public class Configuration
{
    public static void Configure()
    {
        Mapper.Initialize(cfg =>
        {
            cfg.AddProfile<Profiles.SourceProfile>();
            cfg.AddProfile<Profiles.OrderProfile>();
            cfg.AddProfile<Profiles.CalendarEventProfile>();
        });
    }
}

在程序运行的时候,只需要调用Configure方法即可。

了解了这些实现以后,我们可以再项目中添加AutoMapper文件夹,文件夹结构如下:

Configuration为我们的静态配置入口类;Profiles文件夹为我们所有Profile类的文件夹。

AutoMapper.Configuration.Configure();

扁平化映射(Flattening)

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

Order类:

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

    public decimal GetTotal()
    {
        return 100M;
    }
}

Order类中包含了一个customer对象和一个GetTotal方法,为了方便演示,我直接将GetTotal方法返回100;

Customer类的定义如下:

public class Customer
{
    public string Name { get; set; }
}

OrderDto类的定义如下:

public class OrderDto
{
    public string CustomerName { get; set; }
    public string Total { 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);
ObjectDumper.Write(order, 2);
ObjectDumper.Write(orderDto);

测试结果:

指定映射字段(Projection)

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

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

public class CalendarEventForm
{
    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.CalendarEventForm>()
            .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 calendarEventForm = Mapper.Map<Entity.CalendarEventForm>(calendarEvent);
ObjectDumper.Write(calendarEventForm);

测试结果:

验证配置项(Configuration Validation)

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

验证的用法:

Mapper.AssertConfigurationIsValid();

例如:

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

Destination代码:

public class Destination
{
    public int SomeValuefff { get; set; }
}

测试:

Mapper.CreateMap<Entity.Source, Entity.Destination>();
Mapper.AssertConfigurationIsValid();

运行程序将会出现AutoMapperConfigurationException异常:

这是因为SomeValuefff在Source类中没有对应的字段造成的。

解决这种异常的方法有:

指定映射字段,例如:

Mapper.CreateMap<Entity.Source, Entity.Destination>()
    .ForMember(dest => dest.SomeValuefff, opt =>
    {
        opt.MapFrom(src => src.SomeValue);
    });

或者使用Ignore方法:

Mapper.CreateMap<Entity.Source, Entity.Destination>()
    .ForMember(dest => dest.SomeValuefff, opt =>
    {
        opt.Ignore();
    });

或者使用自定义解析器,自定义解析器在下面讲到。

自定义解析器(Custom value resolvers)

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

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

public class Destination
{
    public int Total { get; set; }
}

Total属性在Source中不存在,如果现在创建映射规则,在映射的时候必然会抛出异常。这个时候我们就需要使用自定义解析器来完成映射。

自定义解析器需要实现 IValueResolver 接口,接口的定义如下:

public interface IValueResolver
{
    ResolutionResult Resolve(ResolutionResult source);
}

我们来自定义一个Resolver:

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, Destination>()
            .ForMember(dest => dest.Total, opt =>
            {
                opt.ResolveUsing<CustomResolver>();
            });
    }
}

测试代码:

Source src = new Source()
{
    Value1 = 1,
    Value2 = 2
};
Destination dest = Mapper.Map<Destination>(src);
ObjectDumper.Write(dest);

测试结果:

在使用自定义Resolver中,我们还可以指定Resolver的构造函数,例如:

//Source->Destination
CreateMap<Source, Destination>()
    .ForMember(dest => dest.Total, opt =>
    {
        opt.ResolveUsing<CustomResolver>()
            .ConstructedBy(() => new CustomResolver());
    });

自定义类型转换器(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>;

当我们有如下的Source类和Destination类:

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

public class Destination
{
    public int Value1 { get; set; }
}

我们可以使用如下配置:

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

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

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

public class CustomConverter : ITypeConverter<Source, Destination>
{
    public Destination Convert(ResolutionContext context)
    {
        Source src = context.SourceValue as Source;
        Destination dest = new Destination();
        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, Destination>()
            .ConvertUsing<CustomConverter>();
    }
}

或者,我们也可以使用下面的配置:

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

空值替换(Null substitution)

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

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

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

配置代码:

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

测试代码:

Source src = new Source();
Destination dest = Mapper.Map<Destination>(src);
ObjectDumper.Write(dest);

测试结果:

条件映射(Conditional mapping)

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

public class Foo
{
    public int baz;
}

public class Bar
{
    public uint baz;
}

对应的配置代码如下:

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

总结

关于AutoMapper更多信息 还可移步官网 http://automapper.org/

原文地址:https://www.cnblogs.com/zhangxiaoyong/p/10368906.html

时间: 2024-10-18 14:53:11

.NET Core中使用AutoMapper的相关文章

ASP.NET Core Web 应用程序系列(五)- 在ASP.NET Core中使用AutoMapper进行实体映射

原文:ASP.NET Core Web 应用程序系列(五)- 在ASP.NET Core中使用AutoMapper进行实体映射 本章主要简单介绍下在ASP.NET Core中如何使用AutoMapper进行实体映射.在正式进入主题之前我们来看下几个概念: 1.数据库持久化对象PO(Persistent Object):顾名思义,这个对象是用来将我们的数据持久化到数据库,一般来说,持久化对象中的字段会与数据库中对应的 table 保持一致. 2.视图对象VO(View Object):视图对象 V

ASP.NET.Core中使用AutoMapper

首先需要在NuGet中引用AutoMapper的类库 install-package AutoMapper install-package AutoMapper.Extensions.Microsoft.DependencyInjection 然后创建好要进行转换的类 public class User { public int ID { get; set; } public string Name { get; set; } } public class UserDto { public in

在ASP.NET Core中使用AutoMapper

普通的模型映射 现在有两个类,实体类Student和返回展示的 View类StudentView 两个实体类字段还基本可以,不是很多,假如字段非常非常多,还需要手动去赋值的话,简直太痛苦了. 想了想这才是一个方法,一般的系统都会少则几十,多则上百个这样的方法,这还不算,大家肯定遇到过一个情况,如果有一天要在页面多显示一个字段,噗!不是吧,首先要存在数据库,然后在该实体类就应该多一个,然后再在每一个赋值的地方增加一个,而且也没有更好的办法不是,一不小心就少了一个,然后被产品测试说咱们不细心,心塞哟

.NET Core中的CSV解析库

感谢 本篇首先特别感谢从此启程兄的<.NetCore外国一些高质量博客分享>, 发现很多国外的.NET Core技术博客资源, 我会不定期从中选择一些有意思的文章翻译总结一下. .NET Core中的CSV解析库 本篇博客来源于.NET Core Totorials的<CSV Parsing In .NET Core>. 背景介绍 对于初级程序员来说, 使用string.Split(',')来解析CSV文件基本就是唯一可行的方法, 但是之后他们会发现除了使用逗号分隔值之外,CSV中

.NET Core中ADO.NET SqlClient的使用与常见问题

一.简介 在很多要求性能的项目中,我们都要使用传统的ADO.NET的方式来完成我们日常的工作:目前有一些网友问有关于.NET Core操作SQL Server的问题在本文中解答一下. 本文旨在指出,在.NET Core中使用ADO.NET SqlClient操作SQL SERVER数据库时的一些常见的问题,在本文的第三部分,如果只关心解决问题,请跳过第两部分进行阅读. 二.使用ADO.NET 首先建立好一个ASP.NET MVC Core Project 或 .NET Core Class Li

[翻译]在 .NET Core 中的并发编程

原文地址:http://www.dotnetcurry.com/dotnet/1360/concurrent-programming-dotnet-core 今天我们购买的每台电脑都有一个多核心的 CPU,允许它并行执行多个指令.操作系统通过将进程调度到不同的内核来发挥这个结构的优点.然而,还可以通过异步 I/O 操作和并行处理来帮助我们提高单个应用程序的性能.在.NET Core中,任务 (tasks) 是并发编程的主要抽象表述,但还有其他支撑类可以使我们的工作更容易. 并发编程 - 异步 v

Asp.net core中的websocket

Websocket是html5后的产物,对于asp.net core中也得到了支持,首先在NuGet中添加Microsoft.AspNetCore.WebSockets的引用(现在是1.0.1版本,2017年3月7日发布的). 首先在Configure中添加中间件 //添加websocket中间件 app.UseWebSockets(); 接下来就要定义自己处理websocket的中间件了,代码如下: using Microsoft.AspNetCore.Http; using System;

NET Core中怎么使用HttpContext.Current

NET Core中怎么使用HttpContext.Current 阅读目录 一.前言 二.IHttpContextAccessor 三.HttpContextAccessor 回到目录 一.前言 我们都知道,ASP.NET Core作为最新的框架,在MVC5和ASP.NET WebForm的基础上做了大量的重构.如果我们想使用以前版本中的HttpContext.Current的话,目前是不可用的,因为ASP.NET Core中是并没有这个API的. 当然我们也可以通过在Controller中访问

ASP.NET Core中的依赖注入(5): ServiceProvider实现揭秘 【解读ServiceCallSite 】

通过上一篇的介绍我们应该对实现在ServiceProvider的总体设计有了一个大致的了解,但是我们刻意回避一个重要的话题,即服务实例最终究竟是采用何种方式提供出来的.ServiceProvider最终采用何种方式提供我们所需的服务实例取决于最终选择了怎样的ServiceCallSite,而服务注册是采用的ServiceDescriptor有决定了ServiceCallSite类型的选择.我们将众多不同类型的ServiceCallSite大体分成两组,一组用来创建最终的服务实例,另一类则与生命周