简体   繁体   English

AutoMapper 版本 10 和 ASP.NET MVC

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM