.Net Core 3.1 上的 AutoMapper
AutoMapper on .Net Core 3.1
在 Net Core 3.1 应用程序中,我正在尝试使用 AutoMapper.Extensions.Microsoft.DependencyInjection 7。
在解决方案中,我有 3 个项目:
- 内容(开始项目)
- 核心
- Entity framework
安装nuget后,这是我的代码:
Startup.cs 在内容项目中:
using AutoMapper;
using Project.Content.EntityFrameWork;
using Project.Content.Dto;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
namespace Project.Content
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
// Auto Mapper Configurations
services.AddAutoMapper(typeof(AutoMapping));
string connectionString = Configuration["ConnectionString:Default"];
services.AddDbContext<ProjectContext>(options =>
options.UseSqlServer(connectionString));
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
}
AutoMapping.cs 在内容项目中:
using AutoMapper;
using Project.Content.Core.Domain.Model;
namespace Project.Content.Dto
{
class AutoMapping : Profile
{
public AutoMapping()
{
CreateMap<Exercise, ExerciseDto>();
CreateMap<ExerciseDto, Exercise>();
}
}
}
这是我要映射的控制器:
using AutoMapper;
using Project.Content.EntityFrameWork;
using Project.Content.Dto;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
namespace Project.Content.Controllers
{
[ApiController]
[Route("/exercise")]
public class ExercisesController : ControllerBase
{
private readonly ILogger<ExercisesController> _logger;
private ProjectContext _dbContext;
private IMapper _mapper;
public ExercisesController(ILogger<ExercisesController> logger, ProjectContext dbContext, IMapper mapper)
{
_logger = logger;
_dbContext = dbContext;
_mapper = mapper;
}
[HttpGet("{id}")]
public ExerciseDto GetById(int id)
{
var exercise = _mapper.Map<ExerciseDto>(_dbContext.Exercises.Where(x => x.Id == id));
return exercise;
}
}
}
在此控制器中,当它尝试映射对象时显示错误:
AutoMapper.AutoMapperMappingException: Missing type map configuration
or unsupported mapping.
Mapping types: EntityQueryable1 -> ExerciseDto
Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryable
1[[Project.Content.Core.Domain.Model.Exercise,
Project.Content.Core, Version=1.0.0.0, Culture=neutral,
PublicKeyToken=null]] -> Project.Content.Dto.ExerciseDto at
lambda_method(Closure , EntityQueryable`1 , ExerciseDto ,
ResolutionContext ) at lambda_method(Closure , Object , Object ,
ResolutionContext ) at
Project.Content.Controllers.ExercisesController.GetById(Int32 id) in
C:\Projects\Project\Project.Content\Project.Content.Service\Controllers\ExercisesController.cs:line
44 at lambda_method(Closure , Object , Object[] ) at
Microsoft.Extensions.Internal.ObjectMethodExecutor.Execute(Object
target, Object[] parameters) at
Microsoft.AspNetCore.Mvc.Infrastructure.ActionMethodExecutor.SyncObjectResultExecutor.Execute(IActionResultTypeMapper
mapper, ObjectMethodExecutor executor, Object controller, Object[]
arguments) at
Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.InvokeActionMethodAsync()
at
Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Next(State&
next, Scope& scope, Object& state, Boolean& isCompleted) at
Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.InvokeNextActionFilterAsync()
--- End of stack trace from previous location where exception was thrown --- at
Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Rethrow(ActionExecutedContextSealed
context) at
Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Next(State&
next, Scope& scope, Object& state, Boolean& isCompleted) at
Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.InvokeInnerFilterAsync()
--- End of stack trace from previous location where exception was thrown --- at
Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.g__Awaited|19_0(ResourceInvoker
invoker, Task lastTask, State next, Scope scope, Object state, Boolean
isCompleted) at
Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.g__Logged|17_1(ResourceInvoker
invoker) at
Microsoft.AspNetCore.Routing.EndpointMiddleware.g__AwaitRequestTask|6_0(Endpoint
endpoint, Task requestTask, ILogger logger) at
Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext
context) at
Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext
context)
您正在尝试映射 IQueryable<T>
,因为它使用 deferred execution。在尝试映射之前,您需要使用 ToList()
或 ToListAsync()
之类的内容来执行查询。
您还试图将集合映射到单个项目。您应该映射到一个集合。最终结果看起来像这样
[HttpGet("{id}")]
public ExerciseDto GetById(int id)
{
var exercises = _dbContext.Exercises.Where(x => x.Id == id).ToList();
return _mapper.Map<IEnumerable<ExerciseDto>>(exercises);
}
或者,您可以利用 AutoMappers Queryable Extensions 执行投影,这可能会带来更好的 SQL 性能,因为它会尝试仅查询必要的数据。这可能看起来像这样
[HttpGet("{id}")]
public ExerciseDto GetById(int id)
{
return _mapper.ProjectTo<ExerciseDto>(_dbContext.Exercises.Where(x => x.Id == id)).ToList();
}
附带说明一下,如果您希望此查询是单个对象,或者如果它不存在则找不到,您可以使用 FirstOrDefault
而不是 Where
。此外,您可以 return 一个 IActionResult 来利用基础控制器结果,例如 NotFound
或 Ok
.
如果您使用 Automapper 包 9.0.0 或更高版本,您将需要显式配置地图。
我解决了将 AutoMapper 降级到 8.0.0 和 AutoMapper.Extensions.Microsoft.DependencyInjection 到版本 6.0.0
在 Net Core 3.1 应用程序中,我正在尝试使用 AutoMapper.Extensions.Microsoft.DependencyInjection 7。 在解决方案中,我有 3 个项目:
- 内容(开始项目)
- 核心
- Entity framework
安装nuget后,这是我的代码:
Startup.cs 在内容项目中:
using AutoMapper;
using Project.Content.EntityFrameWork;
using Project.Content.Dto;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
namespace Project.Content
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
// Auto Mapper Configurations
services.AddAutoMapper(typeof(AutoMapping));
string connectionString = Configuration["ConnectionString:Default"];
services.AddDbContext<ProjectContext>(options =>
options.UseSqlServer(connectionString));
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
}
AutoMapping.cs 在内容项目中:
using AutoMapper;
using Project.Content.Core.Domain.Model;
namespace Project.Content.Dto
{
class AutoMapping : Profile
{
public AutoMapping()
{
CreateMap<Exercise, ExerciseDto>();
CreateMap<ExerciseDto, Exercise>();
}
}
}
这是我要映射的控制器:
using AutoMapper;
using Project.Content.EntityFrameWork;
using Project.Content.Dto;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
namespace Project.Content.Controllers
{
[ApiController]
[Route("/exercise")]
public class ExercisesController : ControllerBase
{
private readonly ILogger<ExercisesController> _logger;
private ProjectContext _dbContext;
private IMapper _mapper;
public ExercisesController(ILogger<ExercisesController> logger, ProjectContext dbContext, IMapper mapper)
{
_logger = logger;
_dbContext = dbContext;
_mapper = mapper;
}
[HttpGet("{id}")]
public ExerciseDto GetById(int id)
{
var exercise = _mapper.Map<ExerciseDto>(_dbContext.Exercises.Where(x => x.Id == id));
return exercise;
}
}
}
在此控制器中,当它尝试映射对象时显示错误:
AutoMapper.AutoMapperMappingException: Missing type map configuration or unsupported mapping.
Mapping types: EntityQueryable
1 -> ExerciseDto Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryable
1[[Project.Content.Core.Domain.Model.Exercise, Project.Content.Core, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]] -> Project.Content.Dto.ExerciseDto at lambda_method(Closure , EntityQueryable`1 , ExerciseDto , ResolutionContext ) at lambda_method(Closure , Object , Object , ResolutionContext ) at Project.Content.Controllers.ExercisesController.GetById(Int32 id) in C:\Projects\Project\Project.Content\Project.Content.Service\Controllers\ExercisesController.cs:line 44 at lambda_method(Closure , Object , Object[] ) at Microsoft.Extensions.Internal.ObjectMethodExecutor.Execute(Object target, Object[] parameters) at Microsoft.AspNetCore.Mvc.Infrastructure.ActionMethodExecutor.SyncObjectResultExecutor.Execute(IActionResultTypeMapper mapper, ObjectMethodExecutor executor, Object controller, Object[] arguments) at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.InvokeActionMethodAsync() at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted) at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.InvokeNextActionFilterAsync() --- End of stack trace from previous location where exception was thrown --- at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Rethrow(ActionExecutedContextSealed context) at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted) at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.InvokeInnerFilterAsync() --- End of stack trace from previous location where exception was thrown --- at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.g__Awaited|19_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted) at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.g__Logged|17_1(ResourceInvoker invoker) at Microsoft.AspNetCore.Routing.EndpointMiddleware.g__AwaitRequestTask|6_0(Endpoint endpoint, Task requestTask, ILogger logger) at Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext context) at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context)
您正在尝试映射 IQueryable<T>
,因为它使用 deferred execution。在尝试映射之前,您需要使用 ToList()
或 ToListAsync()
之类的内容来执行查询。
您还试图将集合映射到单个项目。您应该映射到一个集合。最终结果看起来像这样
[HttpGet("{id}")]
public ExerciseDto GetById(int id)
{
var exercises = _dbContext.Exercises.Where(x => x.Id == id).ToList();
return _mapper.Map<IEnumerable<ExerciseDto>>(exercises);
}
或者,您可以利用 AutoMappers Queryable Extensions 执行投影,这可能会带来更好的 SQL 性能,因为它会尝试仅查询必要的数据。这可能看起来像这样
[HttpGet("{id}")]
public ExerciseDto GetById(int id)
{
return _mapper.ProjectTo<ExerciseDto>(_dbContext.Exercises.Where(x => x.Id == id)).ToList();
}
附带说明一下,如果您希望此查询是单个对象,或者如果它不存在则找不到,您可以使用 FirstOrDefault
而不是 Where
。此外,您可以 return 一个 IActionResult 来利用基础控制器结果,例如 NotFound
或 Ok
.
如果您使用 Automapper 包 9.0.0 或更高版本,您将需要显式配置地图。
我解决了将 AutoMapper 降级到 8.0.0 和 AutoMapper.Extensions.Microsoft.DependencyInjection 到版本 6.0.0