如何在ASP.NET Core中设置Automapper

365

我在.NET方面相对较新,决定学习.NET Core而非传统方式。我发现了一篇详细的文章 在此设置AutoMapper,但是否有更简单的教程适合新手?


5
请访问 https://dotnetcoretutorials.com/2017/09/23/using-automapper-asp-net-core/,该文章介绍了如何在ASP.NET Core中使用AutoMapper实现对象映射。 - Michael Freidgeim
对于更新的核心版本(>v1),请查看@Saineshwar的答案 https://dev59.com/cVkS5IYBdhLWcg3wKzqq#53455699 - Robbie
1
一个完整的答案,附带示例 点击此链接 - Iman Bahrampour
如果您的项目中已经有了Identity,则已经通过它隐式引用了Automapper。添加AutoMapper.Extensions.Microsoft.DependencyInjection可能会将依赖项更改为较新版本的Automapper并破坏Identity。请注意选择具有相同依赖项的DI版本。另请参见https://dev59.com/Qek5XIcBkEYKwwoY3dX9。 - Daz
1
AutoMapper使用指南,适用于需要深入了解的人 https://jimmybogard.com/automapper-usage-guidelines/ - Kevin Xiong
20个回答

776

我找到答案了!以下是详细信息:

  1. 通过NuGet将主要的AutoMapper软件包添加到您的解决方案中。

  2. 通过NuGet将AutoMapper依赖注入套件添加到您的解决方案中。

  3. 为映射配置创建一个新类(我在主解决方案目录中创建了一个名为MappingProfile.cs的类,并添加了以下代码)。下面以UserUserDto对象为例。

 public class MappingProfile : Profile {
     public MappingProfile() {
         // Add as many of these lines as you need to map your objects
         CreateMap<User, UserDto>();
         CreateMap<UserDto, User>();
     }
 }
然后将AutoMapperConfiguration添加到Startup.cs中,如下所示:
 public void ConfigureServices(IServiceCollection services) {
     // .... Ignore code before this

    // Auto Mapper Configurations
     var mapperConfig = new MapperConfiguration(mc =>
     {
         mc.AddProfile(new MappingProfile());
     });

     IMapper mapper = mapperConfig.CreateMapper();
     services.AddSingleton(mapper);

     services.AddMvc();

 }
要在代码中调用映射对象,请执行以下操作:
 public class UserController : Controller {

     // Create a field to store the mapper object
     private readonly IMapper _mapper;

     // Assign the object in the constructor for dependency injection
     public UserController(IMapper mapper) {
         _mapper = mapper;
     }

     public async Task<IActionResult> Edit(string id) {

         // Instantiate source object
         // (Get it from the database or whatever your code calls for)
         var user = await _context.Users
             .SingleOrDefaultAsync(u => u.Id == id);

         // Instantiate the mapped data transfer object
         // using the mapper you stored in the private field.
         // The type of the source object is the first type argument
         // and the type of the destination is the second.
         // Pass the source object you just instantiated above
         // as the argument to the _mapper.Map<>() method.
         var model = _mapper.Map<UserDto>(user);

         // .... Do whatever you want after that!
     }
 }

5
这篇链接的详细文章(https://lostechies.com/jimmybogard/2016/07/20/integrating-automapper-with-asp-net-core-di/)解释了如何定位`Profile`类,并将其与ASP.NET Core DI集成。 - Kieren Johnstone
32
你可以在任意一个CreateMap行末加上.ReverseMap()来合并这两行。也许需要加上注释,但我认为这样更加直观易懂。 - Astravagrant
6
第三步中提到在顶部添加 "using AutoMapper;" 可能会有帮助,以便导入扩展方法。 - Rocklan
11
这在.NET Core 1.1时运行良好,但一旦升级到.NET Core 2.0,就不再有效。我认为我需要明确指定逻辑配置类程序集。仍在研究如何完成这个过程。更新:啊,答案就在您的评论中,我必须传递typeof类,这是我的配置文件。// services.AddAutoMapper(typeof(Startup)); // <-- 新版Automapper使用此签名 - Esen
4
在AutoMapper v8和Dependency Injection v5附加组件中,唯一需要的是在Startup类的ConfigureServices方法中添加services.AddAutoMapper()行。对于我来说,它甚至能够在依赖的类库项目中找到Profile类。 - stricq
显示剩余13条评论

101

使用AutoMapper与ASP.NET Core的步骤。

步骤1. 从NuGet包安装AutoMapper.Extensions.Microsoft.DependencyInjection。

图片描述

步骤2. 创建一个名为“Mappings”的文件夹以在Solution中保存映射。

图片描述

步骤3. 添加Mapping文件夹后,我们添加了一个名称为“MappingProfile”的类,这个名称可以是任何独特且易于理解的名称。

在这个类中,我们将维护所有映射。

图片描述

步骤4. 在Startup“ConfigureServices”中初始化Mapper

在Startup类中,我们需要初始化我们创建的Profile并注册AutoMapper服务。

  Mapper.Initialize(cfg => cfg.AddProfile<MappingProfile>());

  services.AddAutoMapper();

展示了ConfigureServices方法的代码片段,其中我们需要初始化和注册AutoMapper。

public class Startup
{
    public Startup(IConfiguration configuration)
    {
        Configuration = configuration;
    }

    public IConfiguration Configuration { get; }


    public void ConfigureServices(IServiceCollection services)
    {
        services.Configure<CookiePolicyOptions>(options =>
        {
            // This lambda determines whether user consent for non-essential cookies is needed for a given request.
            options.CheckConsentNeeded = context => true;
            options.MinimumSameSitePolicy = SameSiteMode.None;
        });


        // Start Registering and Initializing AutoMapper

        Mapper.Initialize(cfg => cfg.AddProfile<MappingProfile>());
        services.AddAutoMapper();

        // End Registering and Initializing AutoMapper

        services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);

    }}

步骤 5. 获取输出。

为了获得映射结果,我们需要调用AutoMapper.Mapper.Map并传递适当的目标和源。

AutoMapper.Mapper.Map<Destination>(source);

代码片段

    [HttpPost]
    public void Post([FromBody] SchemeMasterViewModel schemeMaster)
    {
        if (ModelState.IsValid)
        {
            var mappedresult = AutoMapper.Mapper.Map<SchemeMaster>(schemeMaster);
        }
    }

26
我遇到了以下错误:'Mapper' 不包含 'initialize' 的定义。 我正在使用 AutoMapper.Extensions.Microsoft.DependencyInjection 版本 7.0.0。 - kimbaudi
非常详细的回答。谢谢您先生。 - Rod Hartzell
6
如果您正在使用ASP.NET CORE 3.0,请查看此教程:如何在ASP.NET Core 3.0中设置AutoMapper。链接为https://tutexchange.com/how-to-set-up-automapper-in-asp-net-core-3-0/ - Saineshwar Bageri - MVP
如果我想在调用AddAutoMapper之前在MapperConfiguration上调用CompileMappings*,该怎么办? - John Demetriou

57

我希望你能继续完善@theutz的答案 - 特别是这一行:

// services.AddAutoMapper(typeof(Startup));  // <-- newer automapper version uses this signature.

AutoMapper.Extensions.Microsoft.DependencyInjection版本3.2.0存在一个(可能的)错误。(我正在使用.NET Core 2.0)

这个问题在这个GitHub issue中解决。如果你继承AutoMapper的Profile类的类存在于启动类不在的程序集中,那么如果你的AutoMapper注入看起来像这样,它们可能不会被注册:

services.AddAutoMapper();

除非您明确指定要搜索AutoMapper配置文件的程序集,否则不会进行自动发现。

您可以在Startup.ConfigureServices中按以下方式完成此操作:

services.AddAutoMapper(<assembies> or <type_in_assemblies>);

"assemblies""type_in_assemblies"指向您的应用程序中指定Profile类所在的程序集。例如:

services.AddAutoMapper(typeof(ProfileInOtherAssembly), typeof(ProfileInYetAnotherAssembly));

推测(并且我强调这个词)由于以下的无参重载实现(源代码来自GitHub):

public static IServiceCollection AddAutoMapper(this IServiceCollection services)
{
     return services.AddAutoMapper(null, AppDomain.CurrentDomain.GetAssemblies());
}

我们依赖于CLR已经JIT编译了包含AutoMapper配置文件的程序集,但这可能是真的也可能不是真的,因为它们只在需要时才被JIT编译(更多详细信息请参见StackOverflow问题)。


9
这是针对最新版本的AutoMapper和AspNetCore的正确答案。 - Joshit
1
这就是我在AutoMapper 8.1(最新版本)中寻找的答案。 - Tinaira
如果我想在调用AddAutoMapper之前在MapperConfiguration上调用CompileMappings*,该怎么办? - John Demetriou
FYI:1)这个答案说theutz的答案引用了AddAutoMapper(),但实际上并没有;2)无参的AddAutoMapper()扩展方法已被移除;我正在使用11.0版本……顺便说一句,自动映射API似乎随着时间的推移而经常发生变化(这很烦人),我认为theutz在某个时候更新了他的答案。 - steve

36

我是这样解决的(与上面类似,但我觉得这是一种更简洁的解决方案)。适用于 .NET Core 3.x

创建MappingProfile.cs类,并填充构造函数与映射(我计划使用一个类来保存所有我的映射)。

    public class MappingProfile : Profile
    {
        public MappingProfile()
        {
            CreateMap<Source, Dest>().ReverseMap();
        }
    }

在 Startup.cs 中添加以下内容以添加到 DI(程序集参数是保存映射配置的类,在我的情况下,它是 MappingProfile 类)。

//add automapper DI
services.AddAutoMapper(typeof(MappingProfile));

在控制器中,使用它就像使用其他依赖注入对象一样

    [Route("api/[controller]")]
    [ApiController]
    public class AnyController : ControllerBase
    {
        private readonly IMapper _mapper;

        public AnyController(IMapper mapper)
        {
            _mapper = mapper;
        }
        
        public IActionResult Get(int id)
        {
            var entity = repository.Get(id);
            var dto = _mapper.Map<Dest>(entity);
            
            return Ok(dto);
        }
    }



2
我喜欢你的回答。我认为像这个答案中所示,用new Type[] {}包装MappingProfiles是不必要的。 - Second Person Shooter
1
不要忘记在开始时安装 DI:AutoMapper.Extensions.Microsoft.DependencyInjection - John Jang
在您的 Startup.cs 中还需要添加 services.AddScoped<IMapper,Mapper>();。IMapper 是 AutoMapper 接口,而 Mapper 则来自 AutoMapper,因此我们不需要做任何事情。 - milos

32

theutz的回答非常好,我只想补充一点:

如果你让你的映射配置从MapperConfigurationExpression继承而不是Profile,那么你可以非常简单地添加一个测试来验证你的映射设置,这总是很方便的:

[Fact]
public void MappingProfile_VerifyMappings()
{
    var mappingProfile = new MappingProfile();

    var config = new MapperConfiguration(mappingProfile);
    var mapper = new Mapper(config);

    (mapper as IMapper).ConfigurationProvider.AssertConfigurationIsValid();
}

我遇到了一个错误:“AutoMapper扩展的依赖注入与ASP.NET Core 1.1不兼容”。请帮我解决! - Rohit Arora
“verify”的定义似乎存在争议。当某些属性被有意省略以防止映射时,这会导致程序崩溃。 - Jeremy Holovacs
2
如果您不想映射某个属性,请使用.Ignore()进行设置。这样,它会强制您积极思考如何处理每种情况-确保在进行更改时不会错过任何内容。实际上非常实用。因此,验证测试比许多人意识到的要更安全可靠。它并非百分之百可靠,但可以照顾到前90%的情况。 - Arve Systad

18

我喜欢很多答案,特别是@saineshwar的答案。我正在使用.NET Core 3.0和AutoMapper 9.0,所以我觉得现在是更新它的答案的时候了。

对我有用的是在Startup.ConfigureServices(...)中以这种方式注册服务:

    services.AddAutoMapper(cfg => cfg.AddProfile<MappingProfile>(), 
                               AppDomain.CurrentDomain.GetAssemblies());

我认为@saineshwar的回答其余部分非常完美。但如果有人感兴趣,我的控制器代码如下:

[HttpGet("{id}")]
public async Task<ActionResult> GetIic(int id)
{
    // _context is a DB provider
    var Iic = await _context.Find(id).ConfigureAwait(false);

    if (Iic == null)
    {
        return NotFound();
    }

    var map = _mapper.Map<IicVM>(Iic);

    return Ok(map);
}

我的映射类:

public class MappingProfile : Profile
{
    public MappingProfile()
    {
        CreateMap<Iic, IicVM>()
            .ForMember(dest => dest.DepartmentName, o => o.MapFrom(src => src.Department.Name))
            .ForMember(dest => dest.PortfolioTypeName, o => o.MapFrom(src => src.PortfolioType.Name));
            //.ReverseMap();
    }
}

-----编辑-----

阅读Lucian Bargaoanu在评论中提供的文档后,我认为最好稍微修改一下这个答案。

不带参数的services.AddAutoMapper()(@saineshwar回答中提到的)不再起作用(至少对我来说是这样)。但如果使用NuGet程序包AutoMapper.Extensions.Microsoft.DependencyInjection,则框架能够检查所有扩展AutoMapper.Profile的类(例如我的MappingProfile)。

因此,在我的情况下,由于该类属于同一执行程序集,服务注册可以缩短为:services.AddAutoMapper(System.Reflection.Assembly.GetExecutingAssembly());
(更优雅的方法可能是一个不带参数的扩展与这个编码)。

谢谢,Lucian!


2
http://docs.automapper.org/en/latest/Dependency-injection.html#asp-net-core - Lucian Bargaoanu

18

在最新版本的asp.net core中,你应该使用以下初始化:

services.AddAutoMapper(typeof(YourMappingProfileClass));

谢谢。这个方案可行。我正在使用Net core 3.0。我的对象甚至不太复杂,仅具有2个属性:id和name。由于某种原因,在YouTube的某个教程中,我完全按照该项目,包括此行代码“AppDomain.CurrentDomain.GetAssemblies()”,它就奏效了。但是,当我从头开始时,您的解决方案起作用。 - user12345
1
我们可以使用 services.AddAutoMapper(Assembly.GetExecutingAssembly()); 来获取此程序集中从 Profile 类派生的所有映射类。 - Sayyed Dawood

13

需要安装一个包来设置automapper。

dotnet add package AutoMapper.Extensions.Microsoft.DependencyInjection

在使用AddAutoMapper前,它将可用于服务中。

public void ConfigureServices(IServiceCollection services)
{
     services.AddAutoMapper(typeof(Startup));
}

从Employee类创建到EmployeeDTO的映射器。

using AutoMapper;

public class AutomapperProfile: Profile
{
    public AutomapperProfile()
    {
        //Source to destination.
        CreateMap<Employee,EmployeeDTO>();
    }
}

EmployeeController将Employee对象映射到EmployeeDTo对象。

using System.Collections.Generic;
using AutoMapper;
using Microsoft.AspNetCore.Mvc;

[Route("api/[controller]")]
[ApiController()]
public class EmployeeController : ControllerBase
{
    private readonly IMapper _mapper;

    public EmployeeController(IMapper mapper)
    {
        _mapper = mapper;
    }

    [HttpGet]
    public IEnumerable<EmployeeDTO> GetEmployees()
    {
        /* 
        Assume it to be a  service call/database call
        it returns a list of employee, and now we will map it to EmployeeDTO
        */
        var employees = Employee.SetupEmployee();
        var employeeDTO = _mapper.Map<IEnumerable<EmployeeDTO>>(employees);
        return employeeDTO;

    }
}

员工.cs供参考

using System.Collections.Generic;

public class Employee
{
    public int EmployeeId { get; set; }
    public string EmployeeName { get; set; }
    public int Salary { get; set; }

    public static IEnumerable<Employee> SetupEmployee()
    {
        return new List<Employee>()
        {
            new Employee(){EmployeeId = 1, EmployeeName ="First", Salary=10000},
            new Employee(){EmployeeId = 2, EmployeeName ="Second", Salary=20000},
            new Employee(){EmployeeId = 3, EmployeeName ="Third", Salary=30000},
            new Employee(){EmployeeId = 4, EmployeeName ="Fourth", Salary=40000},
            new Employee(){EmployeeId = 5, EmployeeName ="Fifth", Salary=50000}
        };
    }

}

请参考EmployeeDTO.cs

public class EmployeeDTO
{
    public int EmployeeId { get; set; }
    public string EmployeeName { get; set; }
}

10

在我的Startup.cs文件中(Core 2.2,Automapper 8.1.1)

services.AddAutoMapper(new Type[] { typeof(DAL.MapperProfile) });            
在我的数据访问项目中
namespace DAL
{
    public class MapperProfile : Profile
    {
        // place holder for AddAutoMapper (to bring in the DAL assembly)
    }
}

在我的模型定义中

namespace DAL.Models
{
    public class PositionProfile : Profile
    {
        public PositionProfile()
        {
            CreateMap<Position, PositionDto_v1>();
        }
    }

    public class Position
    {
        ...
    }

为什么不直接使用 services.AddAutoMapper( typeof(DAL.MapperProfile) ); 而不是 services.AddAutoMapper(new Type[] { typeof(DAL.MapperProfile) }); - Second Person Shooter

10

对于AutoMapper 9.0.0版本:

public static IEnumerable<Type> GetAutoMapperProfilesFromAllAssemblies()
    {
        foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
        {
            foreach (var aType in assembly.GetTypes())
            {
                if (aType.IsClass && !aType.IsAbstract && aType.IsSubclassOf(typeof(Profile)))
                    yield return aType;
            }
        }
    }

映射配置文件:

public class OrganizationProfile : Profile
{
  public OrganizationProfile()
  {
    CreateMap<Foo, FooDto>();
    // Use CreateMap... Etc.. here (Profile methods are the same as configuration methods)
  }
}
在你的创业公司中:
services.AddAutoMapper(GetAutoMapperProfilesFromAllAssemblies()
            .ToArray());

在控制器或服务中:

注入 Mapper:

private readonly IMapper _mapper;

使用方法:

var obj = _mapper.Map<TDest>(sourceObject);

真棒 @Nicolae Lupei - Velkumar

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接