[英]AutoMapper version 10 and ASP.NET MVC
I have an application I haven't touched for a while and it is giving me some grief.我有一段时间没有碰过一个应用程序,它让我有些悲伤。
When I call the index method of the controller I get the following error:当我调用 controller 的索引方法时,出现以下错误:
I am not sure what I am doing wrong but it would seem that AutoMapper is having trouble mapping a collection of Shift
objects to a ShiftViewModel
.我不确定我做错了什么,但 AutoMapper 似乎无法将Shift
对象的集合映射到ShiftViewModel
。
I have included some snippets below.我在下面包含了一些片段。
Thoughts?想法?
My controller:我的 controller:
using AutoMapper;
using My.DataAccess;
using My.Entity.DatabaseEntities;
using My.Entity.ViewModels;
using My.Service;
using System.Net;
using System.Threading.Tasks;
using System.Web.Mvc;
namespace My.Controllers
{
public class ShiftController : Controller
{
//initialize service object
readonly IShiftService _shiftService;
private readonly IMapper _mapper;
public ShiftController(IShiftService shiftService, IMapper mapper)
{
_shiftService = shiftService;
_mapper = mapper;
}
readonly ApplicationDataManager db = new ApplicationDataManager();
// GET: /Shifts/
public ActionResult Index()
{
var shifts = _shiftService.GetAll();
if (shifts == null)
{
return HttpNotFound();
}
var model = _mapper.Map<ShiftViewModel>(shifts);
return View(model);
}
}
}
Shift
database entity: Shift
数据库实体:
using System;
using System.ComponentModel.DataAnnotations;
namespace My.Entity.DatabaseEntities
{
public class Shift : AuditableEntity<long>
{
[Required, StringLength(6)]
[Editable(true)]
public string Code { get; set; }
public string Detail { get; set; }
public DateTime Start { get; set; }
public long Duration { get; set; }
}
}
ShiftViewModel
class: ShiftViewModel
class:
using System;
using System.ComponentModel.DataAnnotations;
namespace My.Entity.ViewModels
{
public class ShiftViewModel
{
public int Id { get; set; }
public string Code { get; set; }
public string Detail { get; set; }
public DateTime Start { get; set; }
[Display(Name = "Duration of shift")]
[DisplayFormat(DataFormatString = "{0:HH:mm}", ApplyFormatInEditMode = true)]
[DataType(DataType.Duration)]
public string DurationTime
{
get
{
var ts = new TimeSpan(Duration);
var h = ts.Hours == 1 ? "hour" : "hours";
var m = ts.Minutes == 1 ? "min" : "mins";
return string.Format("{0} {1} {2} {3}", ts.Hours, h, ts.Minutes, m);
}
}
public long Duration { get; set; }
}
}
Global.asax
: Global.asax
:
using Autofac;
using Autofac.Integration.Mvc;
using My.DataAccess.Modules;
using System.Web.Mvc;
using System.Web.Optimization;
using System.Web.Routing;
namespace My.App
{
public class MvcApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
//Autofac Configuration
var builder = new ContainerBuilder();
builder.RegisterControllers(typeof(MvcApplication).Assembly).PropertiesAutowired();
builder.RegisterModule(new RepositoryModule());
builder.RegisterModule(new ServiceModule());
builder.RegisterModule(new EFModule());
//Register AutoMapper here using AutoFacModule class (Both methods works)
//builder.RegisterModule(new AutoMapperModule());
builder.RegisterModule<AutoFacModule>();
var container = builder.Build();
DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
}
}
}
AutoFacModule
: AutoFacModule
:
using Autofac;
using AutoFacAndAutoMapperMVC.Infrastructure;
using AutoMapper;
public class AutoFacModule : Module
{
protected override void Load(ContainerBuilder builder)
{
builder.Register(context => new MapperConfiguration(cfg =>
{
//Register Mapper Profile
cfg.AddProfile<AutoMapperProfile>();
}
)).AsSelf().InstancePerRequest();
builder.Register(c =>
{
//This resolves a new context that can be used later.
var context = c.Resolve<IComponentContext>();
var config = context.Resolve<MapperConfiguration>();
return config.CreateMapper(context.Resolve);
})
.As<IMapper>()
.InstancePerLifetimeScope();
}
}
AutoMapperProfile
: AutoMapperProfile
:
using Roster.Entity.DatabaseEntities;
using Roster.Entity.ViewModels;
using AutoMapper;
namespace AutoFacAndAutoMapperMVC.Infrastructure
{
public class AutoMapperProfile : Profile
{
public AutoMapperProfile()
{
CreateMap<Shift, ShiftViewModel>();
CreateMap<ShiftViewModel, Shift>();
}
}
}
Trekco , It is called by a generic method of IEnumerable Trekco ,它由 IEnumerable 的泛型方法调用
public virtual IEnumerable<T> GetAll()
{
return _repository.GetAll();
}
it returns a Shift object它返回一个 Shift object
In you Index
method, the code -在您的Index
方法中,代码 -
var shifts = _shiftService.GetAll();
is definitely not returning a single Shift
object.绝对不会返回单个Shift
object。 I guess, its returning a list/collection of Shift
object.我猜,它返回Shift
object 的列表/集合。 If so, then with the code -如果是这样,那么使用代码 -
var model = _mapper.Map<ShiftViewModel>(shifts);
you are trying to map a list of Shift
object to a single ShiftViewModel
object which is causing the issue.您正在尝试将 map Shift
object 列表转换为导致问题的单个ShiftViewModel
object。
Change the mapping code to -将映射代码更改为 -
var model = _mapper.Map<List<ShiftViewModel>>(shifts);
So this is how I solved the problem.所以这就是我解决问题的方法。 Thanks everyone for the help.感谢大家的帮助。
Solution解决方案
So it seems, as Akos Nagy , points out in his blog post the problem is that AutoMapper and Autofac don't play very well together.因此,正如Akos Nagy在他的博客文章中指出的那样,问题似乎在于 AutoMapper 和 Autofac 不能很好地协同工作。 This wasn't helped by the fact that my code was not very good to begin with.我的代码一开始就不是很好,这无济于事。
The AutoMapper docs had a page on Dependency Injection foundhere . AutoMapper 文档有一个关于 Dependency Injection 的页面,可在此处找到。 There were no real examples for AutoFac however it did point me towards a Nuget package named AutoMapper.Contrib.Autofac.DependencyInjection 5.2.0 . AutoFac 没有真实的例子,但它确实指向了一个名为AutoMapper.Contrib.Autofac.DependencyInjection 5.2.0的 Nuget package 。 There is a GitHub project here . 这里有一个 GitHub 项目。
So I installed that package with the Package Manager Console.所以我用 Package 管理控制台安装了 package。
Install-Package AutoMapper.Contrib.Autofac.DependencyInjection -Version 5.2.0
I then simplified my Shift domain object class.然后我简化了我的 Shift 域 object class。
using System;
using System.ComponentModel.DataAnnotations;
namespace Roster.Entity.DatabaseEntities
{
public class Shift : AuditableEntity<long>
{
#region Public Properties
[Required, StringLength(6)]
[Editable(true)]
[Display(Name = "Code")]
public string Code { get; set; }
public string Detail { get; set; }
public DateTime Start { get; set; }
public long Duration { get; set; }
#endregion Public Properties
}
}
Next I reworked my ViewModel, again just to make things a bit cleaner and adding a little business logic functionality.接下来我重新设计了我的 ViewModel,再次只是为了让事情变得更简洁并添加一些业务逻辑功能。
using System;
using System.ComponentModel.DataAnnotations;
namespace Roster.Entity.ViewModels
{
public class ShiftViewModel : AuditableEntity<long>
{
[Required, StringLength(6)]
[Editable(true)]
[Display(Name = "Code")]
public string Code { get; set; }
public string Detail { get; set; }
[DisplayFormat(DataFormatString = "{0:HH:mm }", ApplyFormatInEditMode = true)]
public DateTime Start { get; set; }
public long Duration { get; set; }
[Display(Name = "Text Duration time of shift")]
[DisplayFormat(DataFormatString = "{0:hh:mm}", ApplyFormatInEditMode = true)]
[DataType(DataType.Duration)]
public string DurationTime
{
get
{
TimeSpan ts = new TimeSpan(Duration);
var h = ts.Hours == 1 ? "hour" : "hours";
var m = ts.Minutes == 1 ? "min" : "mins";
return string.Format("{0} {1} {2} {3}", ts.Hours, h, ts.Minutes, m);
}
}
[Display(Name = "Shift Duration")]
[DisplayFormat(DataFormatString = "{0:hh\\:mm}", ApplyFormatInEditMode = true)]
[DataType(DataType.Duration)]
public string ShiftDuration
{
get
{
return TimeSpan.FromTicks(Duration).ToString();
}
set
{
TimeSpan interval = TimeSpan.FromTicks(Duration);
Duration = interval.Ticks;
}
}
}
}
Now on to mapping the domain object to my ViewModel.现在将域 object 映射到我的 ViewModel。
First I needed to create an AutoMapper profile to create the map.首先,我需要创建一个 AutoMapper 配置文件来创建 map。 I saved this in the APP_Start folder for no reason than I thought it a good place.我无缘无故地将它保存在 APP_Start 文件夹中,因为我认为这是一个好地方。
using AutoMapper;
using Roster.Entity.DatabaseEntities;
using Roster.Entity.ViewModels;
namespace AutoFacAndAutoMapperMVC.Infrastructure
{
public class AutoMapperProfile : Profile
{
public AutoMapperProfile()
{
CreateMap<Shift, ShiftViewModel>()
.ReverseMap();
}
}
}
Next I needed to change Global.asax.cs接下来我需要更改 Global.asax.cs
I registered AutoMapper by adding我通过添加注册了 AutoMapper
builder.RegisterAutoMapper(typeof(MvcApplication).Assembly);
I then added the following lines to resolve the mapper service and control the lifetime scope.然后我添加了以下行来解析映射器服务并控制生命周期 scope。 I am not sure what this actually does but it is recommended in the AutoFac docs here我不确定这实际上是做什么的,但在 此处的 AutoFac 文档中推荐
using(var scope = container.BeginLifetimeScope())
{
var service = scope.Resolve<IMapper>();
}
Finally I altered the Shift Controller to use AutoMapper.最后,我将 Shift Controller 更改为使用 AutoMapper。
using AutoMapper;
using Roster.DataAccess;
using Roster.Entity.ViewModels;
using Roster.Service;
using System.Collections;
using System.Collections.Generic;
using System.Web.Mvc;
namespace Roster.Controllers
{
public class ShiftController : Controller
{
//initialize service object
private readonly IShiftService _shiftService;
private readonly IMapper _mapper;
public ShiftController(IShiftService shiftService, IMapper mapper)
{
_shiftService = shiftService;
_mapper = mapper;
}
readonly ApplicationDataManager db = new ApplicationDataManager();
// GET: /Shifts/
public ActionResult Index()
{
IEnumerable shifts = _shiftService.GetAll();
var model = _mapper.Map<IEnumerable<ShiftViewModel>>(shifts);
if (model == null)
{
return HttpNotFound();
}
return View(model);
}
}
}
Importantly and a real beginners error on my behalf is that because I had a collection of shifts to map I had to have a collection of viewmodels.重要的是,代表我的一个真正的初学者错误是,因为我有一系列到 map 的班次,所以我必须有一系列视图模型。 Thanks atiyar .谢谢阿提亚尔。 I resolved this by mapping like this.我通过这样的映射解决了这个问题。 So stupid on my behalf.代表我太愚蠢了。
var model = _mapper.Map<IEnumerable<ShiftViewModel>>(shifts);
So sorry about the long answer but I thought I would wrap up the question with how I resolved my problem.很抱歉这个长答案,但我想我会用我如何解决我的问题来结束这个问题。 It was a great learning exercise for someone who is not a professional programmer.对于不是专业程序员的人来说,这是一个很好的学习练习。 Thanks everyone.感谢大家。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.