我正在尝试使用 ASP.NET MVC重新制作我不久前制作的应用程序,但我遇到的问题是由使用最新版本的Automapper
引起的。我第一次制作应用程序时遵循了一个教程,在本教程中,我使用的Automapper
版本是 4.1.0。
但是,此版本是在 2016 年左右发布的 - 本教程很旧 - 从那时起Automapper
发生了很多更改,即许多东西现在已经过时了。
在以前的版本中,您可以使用类中的静态CreateMap
方法Mapper
但这已经过时了。
这是我使用旧方法的方法。首先,我创建了一个派生自Profile
的类来存储我的配置。
public class MappingProfile : Profile
{
public MappingProfile()
{
// Domain to DTO
Mapper.CreateMap<Customer, CustomerDto>();
Mapper.CreateMap<Movie, MovieDto>();
Mapper.CreateMap<MembershipType, MembershipTypeDto>();
Mapper.CreateMap<MembershipTypeDto, MembershipType>();
Mapper.CreateMap<Genre, GenreDto>();
Mapper.CreateMap<GenreDto, Genre>();
// Dto to Domain
Mapper.CreateMap<CustomerDto, Customer>()
.ForMember(c => c.Id, opt => opt.Ignore());
Mapper.CreateMap<MovieDto, Movie>()
.ForMember(c => c.Id, opt => opt.Ignore());
}
}
接下来,我初始化了Mapper
类并在Global.asax.cs
中添加了配置文件。
public class MvcApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
// Here's the line of code I added
Mapper.Initialize(c => c.AddProfile<MappingProfile>());
GlobalConfiguration.Configure(WebApiConfig.Register);
AreaRegistration.RegisterAllAreas();
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
}
}
最后,我使用Mapper
中的静态Map
方法将域对象映射到 DTO 对象,反之亦然,在我的 API 控制器中。顺便说一下,这一切都工作正常。
public IHttpActionResult CreateCustomer(CustomerDto customerDto)
{
if (!ModelState.IsValid)
{
BadRequest();
}
var customer = Mapper.Map<CustomerDto, Customer>(customerDto);
_context.Customers.Add(customer);
_context.SaveChanges();
customerDto.Id = customer.Id;
return Created(new Uri(Request.RequestUri + "/" + customer.Id ), customerDto);
}
最新的自动映射器文档建议您使用MapperConfiguration
和CreateMap
为域和 DTO 对象创建映射,并且每个 AppDomain 只需要一个应在启动期间实例化的MapperConfiguration
实例。
我继续并再次创建了一个MappingProfile
来存储我的配置(按照文档的建议),并将此配置包含在Global.asax.cs
的Application_Start()
中,这是 MVC 应用程序的启动。
public class MappingProfile : Profile
{
public MappingProfile()
{
CreateMap<Customer, CustomerDto>();
CreateMap<CustomerDto, Customer>();
}
}
public class MvcApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
// This is the new way of creating a MapperConfiguration
var configuration = new MapperConfiguration(cfg =>
{
cfg.AddProfile<MappingProfile>();
});
IMapper mapper = configuration.CreateMapper();
GlobalConfiguration.Configure(WebApiConfig.Register);
AreaRegistration.RegisterAllAreas();
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
UnityConfig.RegisterComponents();
}
}
此时,文档说您可以使用依赖注入来注入创建的IMapper
实例。 这就是我陷入困境并遇到错误的地方。
该文档很好地涵盖了 ASP.NET Core,解释了如何使用依赖注入,因此我尝试将此方法与UnityConfig
一起使用,其中我在那里创建了MapperConfiguration
并注册了IMapper的实例,然后尝试将其注入到我的Web API控制器中,如下所示:
public static class UnityConfig
{
public static void RegisterComponents()
{
var container = new UnityContainer();
var configuration = new MapperConfiguration(cfg =>
{
cfg.AddProfile<MappingProfile>();
});
IMapper mapper = configuration.CreateMapper();
container.RegisterInstance(mapper);
// register all your components with the container here
// it is NOT necessary to register your controllers
// e.g. container.RegisterType<ITestService, TestService>();
DependencyResolver.SetResolver(new UnityDependencyResolver(container));
}
}
public class MvcApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
GlobalConfiguration.Configure(WebApiConfig.Register);
AreaRegistration.RegisterAllAreas();
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
// I registered the components here inside Global.asax.cs
UnityConfig.RegisterComponents();
}
}
这是我尝试通过依赖注入使用实例的地方。
public class CustomersController : ApiController
{
private ApplicationDbContext _context;
private readonly IMapper _mapper;
public CustomersController()
{
_context = new ApplicationDbContext();
}
public CustomersController(IMapper mapper)
{
_mapper = mapper;
}
// GET /api/customers
public IHttpActionResult GetCustomers()
{
var customersDto = _context.Customers
.Include(c => c.MembershipType)
.ToList()
.Select(_mapper.Map<Customer, CustomerDto>);
return Ok(customersDto);
}
// Remaining code omitted because it's unnecessary
调试时遇到的错误是NullReferenceException
.在"监视"窗口中,我注意到_mapper
为空,但是,对象确实从数据库加载。问题是即使在使用 DI 后_mapper
仍然为空。
有人可以解释一下为什么在 ASP.NET MVC Web API 控制器中以这种方式使用此版本的Automapper
是空的和潜在的修复/提示?
经过大量的阅读和研究,我终于弄清楚了我哪里出错了。我相信Unity.Mvc5
适用于常规控制器,而不是 API 控制器。
出于这个原因,我添加了一个单独的软件包,Unity.AspNet.WebApi
,它带有不同的Unity.Config.cs
。添加包时出现提示,询问我是否要覆盖上一个Unity.Config.cs
(来自Unity.Mvc5
),我选择了是。
从这里,我将我的配置添加到此文件,注册了实例,然后就可以开始了。
这个视频很好地解释了整个事情: https://youtu.be/c38krTX0jeo