【问题标题】:AutoMapper version 10 and ASP.NET MVCAutoMapper 版本 10 和 ASP.NET MVC
【发布时间】:2021-01-31 08:41:14
【问题描述】:

我有一段时间没有碰过一个应用程序,这让我有些难过。

当我调用控制器的 index 方法时,出现以下错误:

我不确定自己做错了什么,但 AutoMapper 似乎无法将 Shift 对象的集合映射到 ShiftViewModel

我在下面添加了一些 sn-ps。

想法?

我的控制器:

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数据库实体:

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类:

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:

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:

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:

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,被IEnumerable的泛型方法调用

public virtual IEnumerable<T> GetAll()
        {
            return _repository.GetAll();
        }

它返回一个 Shift 对象

【问题讨论】:

标签: c# asp.net-mvc automapper viewmodel autofac


【解决方案1】:

在你Index方法中,代码-

var shifts = _shiftService.GetAll();

绝对不会返回单个 Shift 对象。我猜,它返回Shift 对象的列表/集合。如果是这样,那么用代码 -

var model = _mapper.Map<ShiftViewModel>(shifts);

您正在尝试将 Shift 对象列表映射到导致问题的单个 ShiftViewModel 对象。

将映射代码改为-

var model = _mapper.Map<List<ShiftViewModel>>(shifts);

【讨论】:

    【解决方案2】:

    所以这就是我解决问题的方法。感谢大家的帮助。

    解决方案

    看来,正如Akos Nagy 在他的blog post 中指出的那样,问题在于AutoMapper 和Autofac 不能很好地配合使用。我的代码一开始就不是很好,这无济于事。

    AutoMapper 文档有一个关于依赖注入的页面 here。没有 AutoFac 的真实示例,但它确实将我指向了一个名为 AutoMapper.Contrib.Autofac.DependencyInjection 5.2.0 的 Nuget 包。有一个GitHub项目here

    所以我使用包管理器控制台安装了该包。

    Install-Package AutoMapper.Contrib.Autofac.DependencyInjection -Version 5.2.0
    

    然后我简化了我的 Shift 域对象类。

    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
        }
    }
    

    接下来我重新设计了我的 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;
                }
            }
        }
    }
    

    现在将域对象映射到我的 ViewModel。

    首先我需要创建一个 AutoMapper 配置文件来创建地图。我无缘无故地将它保存在 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();
            }
        }
    }
    

    接下来我需要更改 Global.asax.cs

    我通过添加注册了AutoMapper

    builder.RegisterAutoMapper(typeof(MvcApplication).Assembly);
    

    然后我添加了以下几行来解析映射器服务并控制生命周期范围。我不确定这实际上是做什么的,但在 AutoFac 文档 here

    中推荐
    using(var scope = container.BeginLifetimeScope())
    {
      var service = scope.Resolve<IMapper>();
    } 
    

    最后我将 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);
            }
        }
    }
    

    重要的是,对于我来说,一个真正的初学者错误是,因为我有一组要映射的班次,所以我必须有一组视图模型。谢谢atiyar。我通过这样的映射解决了这个问题。代表我太愚蠢了。

    var model = _mapper.Map<IEnumerable<ShiftViewModel>>(shifts);
    

    很抱歉这个冗长的答案,但我想我会用我如何解决我的问题来结束这个问题。对于不是专业程序员的人来说,这是一个很好的学习练习。谢谢大家。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2022-11-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-11-03
      相关资源
      最近更新 更多