如何在ASP.NET Core中设置Automapper


254

我是.NET的新手,所以我决定使用.NET Core,而不是学习“旧方法”。我在这里找到了有关为.NET Core设置AutoMapper的详细文章,但是对于新手来说,是否有更简单的演练?



对于较新版本的core(> v1),请查看@Saineshwar的答案 stackoverflow.com/a/53455699/833878
Robbie,

1
带有示例的完整答案,请单击此链接
Iman Bahrampour

Answers:


552

我想到了!详细信息如下:

  1. 通过NuGet将主要的AutoMapper软件包添加到您的解决方案中。
  2. 通过NuGet将AutoMapper依赖项注入程序包添加到您的解决方案中。

  3. 为映射配置文件创建一个新类。(我在主解决方案目录中创建了一个名为的类,MappingProfile.cs并添加了以下代码。)我将使用Userand UserDto对象作为示例。

    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>();
        }
    }
  4. 然后在中添加AutoMapperConfiguration,Startup.cs如下所示:

    public void ConfigureServices(IServiceCollection services) {
        // .... Ignore code before this
    
       // Auto Mapper Configurations
        var mappingConfig = new MapperConfiguration(mc =>
        {
            mc.AddProfile(new MappingProfile());
        });
    
        IMapper mapper = mappingConfig.CreateMapper();
        services.AddSingleton(mapper);
    
        services.AddMvc();
    
    }
  5. 要在代码中调用映射的对象,请执行以下操作:

    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!
        }
    }

我希望这可以帮助从ASP.NET Core重新开始的人!我欢迎任何反馈或批评,因为我还是.NET世界的新手!


3
链接的详细文章lostechies.com/jimmybogard/2016/07/20/…,解释了Profile课程的位置
Kieren Johnstone

22
@theutz您可以将这两个CreateMap行与.ReverseMap()合并在末尾。也许评论一下,但我觉得它更直观。
Astravagrant

6
在步骤3中提及添加“使用AutoMapper”可能会有所帮助。在顶部,以便导入扩展方法。
罗克兰

8
.net核心1.1可以很好地工作,一旦我升级到.net核心2.0,就不再可用了。我认为,我需要明确指定逻辑配置文件类的程序集。仍在研究如何实现这一目标。更新:嗯,答案取决于您的评论,我必须通过typeof类,这是我的个人资料。// services.AddAutoMapper(typeof(Startup)); // <-较新的自动映射器版本使用此签名
Esen

3
在AutoMapper v8和Dependency Injection v5附加组件中,唯一需要的是services.AddAutoMapper();。Startup类的ConfigureServices方法中的行。对我来说,它甚至能够在从属类库项目中找到Profile类。
stricq

68

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

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

在此处输入图片说明

步骤2.在解决方案中创建一个文件夹,以保留名称为“ Mappings”的映射。

在此处输入图片说明

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

在本课程中,我们将维护所有映射。

在此处输入图片说明

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

在启动类中,我们需要初始化已创建的配置文件,并注册AutoMapper服务。

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

  services.AddAutoMapper();

代码片段显示需要初始化和注册AutoMapper的ConfigureServices方法。

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);
        }
    }

12
我收到以下错误:'Mapper' does not contain a definition for 'initialize'。我正在使用AutoMapper.Extensions.Microsoft.DependencyInjection7.0.0版
kimbaudi

超级详细的答案。谢谢你,先生。
Rod Hartzell

1
如果您使用的是ASP.NET CORE 3.0,请查看本教程,如何在ASP.NET Core 3.0中设置AutoMapper tutexchange.com/how-to-set-up-automapper-in-asp-net-core-3-0
Saineshwar

44

我想扩展@theutz的答案-即这一行:

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

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

这是在解决这个 GitHub的问题。如果您的继承自AutoMapper的Profile类的类在您的Startup类所在的程序集之外,而如果您的AutoMapper注入看起来像这样,则可能不会注册它们:

services.AddAutoMapper();

除非您明确指定要搜索AutoMapper配置文件的程序集。

可以在Startup.ConfigureServices中这样做:

services.AddAutoMapper(<assembies> or <type_in_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问题中的更多信息)。


5
这就是最新版本的AutoMapper和AspNetCore的正确答案
Joshit,

1
这就是我要寻找的AutoMapper 8.1(最新版本)的答案
Tinaira

30

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不兼容”。请帮忙!
罗希特·阿罗拉

似乎“验证”的定义尚待辩论。当设计忽略某些属性以防止映射时,此操作会爆炸。
杰里米·霍洛瓦兹

2
如果您不希望映射属性,请使用.Ignore()进行设置。这样,它迫使您积极考虑处理每种情况-确保在进行更改时不会错过任何内容。实际上超级实用。因此,是的,验证测试是比许多人意识到的更大的安全网。它不是万无一失的,但是它可以处理前90%的问题。
到达Arve Systad

18

我通过.NET Core 2.2 / Automapper 8.1.1 w / Extensions.DI 6.1.1的这种方式(类似于上面的方式解决了该问题,但我觉得这是一个更干净的解决方案)。

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

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

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

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

在Controller中,像使用其他任何DI对象一样使用它

    [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
我喜欢你的回答。我认为包装MappingProfilesnew Type[]{}如图这个答案是不必要的。
面向货币的程序员,

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) });
面向货币的程序员,

8

我喜欢很多答案,尤其是@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());
(一种更优雅的方法可能是使用此编码进行无参数扩展)。

谢谢,露西安!



6

我正在使用AutoMapper 6.1.1和asp.net Core 1.1.2。

首先,定义由Automapper的Profile Class继承的Profile类。我创建了一个IProfile接口,它是空的,目的只是为了找到这种类型的类。

 public class UserProfile : Profile, IProfile
    {
        public UserProfile()
        {
            CreateMap<User, UserModel>();
            CreateMap<UserModel, User>();
        }
    }

现在创建一个单独的类,例如Mappings

 public class Mappings
    {
     public static void RegisterMappings()
     {            
       var all =
       Assembly
          .GetEntryAssembly()
          .GetReferencedAssemblies()
          .Select(Assembly.Load)
          .SelectMany(x => x.DefinedTypes)
          .Where(type => typeof(IProfile).GetTypeInfo().IsAssignableFrom(type.AsType()));

            foreach (var ti in all)
            {
                var t = ti.AsType();
                if (t.Equals(typeof(IProfile)))
                {
                    Mapper.Initialize(cfg =>
                    {
                        cfg.AddProfiles(t); // Initialise each Profile classe
                    });
                }
            }         
        }

    }

现在,在MVC Core Web Project中的Startup.cs文件中,在构造函数中,调用Mapping类,该类将在加载应用程序时初始化所有映射。

Mappings.RegisterMappings();

您可以只在概要文件类中创建子类,并且可以在程序运行services.AddAutoMapper();时创建子类。代码行自动映射器自动知道它们。
isaeid

如果您使用nuget中提供的AutoMapper.Extensions.Microsoft.DependancyInjection,我认为这不是必需的。
Greg Gum

5

对于ASP.NET Core(使用2.0+和3.0测试),如果您希望阅读源文档,请访问:https : //github.com/AutoMapper/AutoMapper.Extensions.Microsoft.DependencyInjection/blob/master/README.md

否则,请按照以下4个步骤操作:

  1. 从nuget安装AutoMapper.Extensions.Microsoft.DependancyInjection。

  2. 只需添加一些配置文件类。

  3. 然后将以下内容添加到您的startup.cs类中。 services.AddAutoMapper(OneOfYourProfileClassNamesHere)

  4. 然后只需将IMapper注入控制器或您需要的任何位置:

public class EmployeesController {

    private readonly IMapper _mapper;

    public EmployeesController(IMapper mapper){

        _mapper = mapper;
    }

如果您现在想简单地使用ProjectTo:

var customers = await dbContext.Customers.ProjectTo<CustomerDto>(_mapper.ConfigurationProvider).ToListAsync()

4

对于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;
            }
        }
    }

MapperProfile:

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());

在控制器或服务中:注入映射器:

private readonly IMapper _mapper;

用法:

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

4

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

services.AddAutoMapper(typeof(YourMappingProfileClass));

2

具有AutoMapper.Extensions.Microsoft.DependencyInjection的Asp.Net Core 2.2。

public class MappingProfile : Profile
{
  public MappingProfile()
  {
      CreateMap<Domain, DomainDto>();
  }
}

在Startup.cs中

services.AddAutoMapper(typeof(List.Handler));

1

添加到提到的Arve Systad进行测试。如果出于某种原因,您喜欢我,并且想要维护theutz解决方案中提供的继承结构,则可以按以下方式设置MapperConfiguration:

var mappingProfile = new MappingProfile();
var config = new MapperConfiguration(cfg =>
{
    cfg.AddProfile(mappingProfile);
});
var mapper = new Mapper(config);

我在NUnit中做到了。


1

services.AddAutoMapper(); 没有为我工作。(我正在使用Asp.Net Core 2.0)

如下配置后

   var config = new AutoMapper.MapperConfiguration(cfg =>
   {                 
       cfg.CreateMap<ClientCustomer, Models.Customer>();
   });

初始化映射器IMapper mapper = config.CreateMapper();

并将映射器对象作为单例服务添加到服务。AddSingleton(mapper);

这样我就可以向控制器添加DI

  private IMapper autoMapper = null;

  public VerifyController(IMapper mapper)
  {              
   autoMapper = mapper;  
  }

我在动作方法中使用了以下内容

  ClientCustomer customerObj = autoMapper.Map<ClientCustomer>(customer);

嗨@venkat,您可能只需要向您的项目添加AutoMapper.Extensions.Microsoft.DependancyInjection程序包
dalcam

-1

关于theutz答案,无需在控制器构造函数中指定IMapper映射器参数。

您可以使用Mapper,因为它在代码的任何位置都是静态成员。

public class UserController : Controller {
   public someMethod()
   {
      Mapper.Map<User, UserDto>(user);
   }
}

11
但是静力学有点可测,不是吗?
Scott Fraley

3
是的 这在许多情况下都可以使用,但是如果在测试中调用此方法时没有配置映射,则会抛出异常(并由于错误的原因导致测试失败)。通过注入,IMapper您可以模拟它,例如,如果与给定测试无关,则使其返回null。
阿维·史塔德
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.