如何在 ASP.NET MVC Web API 控制器中使用 Automapper 10.1.1?
How do I use Automapper 10.1.1 in ASP.NET MVC Web API controller?
我正在尝试重新制作我不久前使用 ASP.NET MVC 制作的应用程序,但我 运行 遇到的问题是由使用最新版本的 Automapper
。我第一次做应用的时候跟着一个教程,在这个教程中,我使用的Automapper
版本是4.1.0.
然而,这个版本是在 2016 年左右发布的——教程很老了——从那时起 Automapper
发生了很多变化,即很多东西现在已经过时了。
在以前的版本中,您可以使用 Mapper
class 中的静态 CreateMap
方法,但现在已过时。
这是我用老方法做的。首先,我创建了一个派生自 Profile
的 class 来存储我的配置。
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
class 并在 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
方法将我的域对象映射到我的 API 控制器中的 DTO 对象,反之亦然。顺便说一下,这一切都很好。
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);
}
latest Automapper documentation 建议您使用 MapperConfiguration
和 CreateMap
为域和 DTO 对象创建映射,并且每个实例只需要一个 MapperConfiguration
应在启动期间实例化的 AppDomain。
我继续创建了一个 MappingProfile
再次存储我的配置 (as recommended by the documentation) 并将此配置包含在 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 尝试将其注入我的网络 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
。在 Watch window 中,我注意到 _mapper
为空,但是,对象确实从数据库中加载。问题是即使在使用 DI 后 _mapper
仍然为 null。
有人可以解释为什么在 ASP.NET MVC Web API 控制器中以这种方式使用此版本的 Automapper
是无效的并且可能 fixes/tips 吗?
经过大量的阅读和研究,我终于弄清楚了哪里出了问题。我相信 Unity.Mvc5
适用于常规控制器而不是 API 控制器。
出于这个原因,我添加了一个单独的包 Unity.AspNet.WebApi
,它带有不同的 Unity.Config.cs
。添加包时出现提示,问我是否要覆盖之前的Unity.Config.cs
(来自Unity.Mvc5
),我选择了
从这里开始,我将配置添加到此文件,注册了实例,一切顺利。
这段视频很好地解释了整个事情:
https://youtu.be/c38krTX0jeo
我正在尝试重新制作我不久前使用 ASP.NET MVC 制作的应用程序,但我 运行 遇到的问题是由使用最新版本的 Automapper
。我第一次做应用的时候跟着一个教程,在这个教程中,我使用的Automapper
版本是4.1.0.
然而,这个版本是在 2016 年左右发布的——教程很老了——从那时起 Automapper
发生了很多变化,即很多东西现在已经过时了。
在以前的版本中,您可以使用 Mapper
class 中的静态 CreateMap
方法,但现在已过时。
这是我用老方法做的。首先,我创建了一个派生自 Profile
的 class 来存储我的配置。
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
class 并在 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
方法将我的域对象映射到我的 API 控制器中的 DTO 对象,反之亦然。顺便说一下,这一切都很好。
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);
}
latest Automapper documentation 建议您使用 MapperConfiguration
和 CreateMap
为域和 DTO 对象创建映射,并且每个实例只需要一个 MapperConfiguration
应在启动期间实例化的 AppDomain。
我继续创建了一个 MappingProfile
再次存储我的配置 (as recommended by the documentation) 并将此配置包含在 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 尝试将其注入我的网络 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
。在 Watch window 中,我注意到 _mapper
为空,但是,对象确实从数据库中加载。问题是即使在使用 DI 后 _mapper
仍然为 null。
有人可以解释为什么在 ASP.NET MVC Web API 控制器中以这种方式使用此版本的 Automapper
是无效的并且可能 fixes/tips 吗?
经过大量的阅读和研究,我终于弄清楚了哪里出了问题。我相信 Unity.Mvc5
适用于常规控制器而不是 API 控制器。
出于这个原因,我添加了一个单独的包 Unity.AspNet.WebApi
,它带有不同的 Unity.Config.cs
。添加包时出现提示,问我是否要覆盖之前的Unity.Config.cs
(来自Unity.Mvc5
),我选择了
从这里开始,我将配置添加到此文件,注册了实例,一切顺利。
这段视频很好地解释了整个事情: https://youtu.be/c38krTX0jeo