尝试从一个域类映射到视图模型类跳过属性

时间:2017-01-19 03:09:29

标签: c# asp.net-core automapper entity-framework-core

我有一个非常奇怪的问题。我正在创建一个ASP.Net Core前端网站和一个ASP.Net Core后端API。后端API使用Entity Framework Core来实现持久性。

我使用视图模型类将数据从网站发布到API,然后我使用AutoMapper从视图模型类映射到实体类,以将其传递到基于EF的存储库。这适用于此项目中与其他数据库表相关的另外两个视图模型/实体类关系。

但是,我正在添加一个新的数据库表和相关的视图模型/实体类集,它给了我这个奇怪的问题。

使用此特定实体/视图模型关系,Automapper失败,没有错误。它只是在数据类型之后跳过任何未在实体类中定义的属性(使属性可为空)。

我创建了一个方法来逐个手动地将属性从视图模型类映射到实体类,我看到了同样的问题,所以我认为它不是特定的AutoMapper问题。

这是我的实体类;

using System;
using System.Collections.Generic;

namespace API.Models
{
    public partial class UserFeaturesEvs
    {
        public int UserId { get; set; }
        public bool Active { get; set; }
        public string LoginNickname { get; set; }
        public string CarrierLoginName { get; set; }
        public string CarrierLoginPassword { get; set; }
        public int CarrierId { get; set; }
        public int? CarrierLoginId { get; set; }
        public string PermitNumber { get; set; }
        public string PermitType { get; set; }
        public string PermitName { get; set; }
        public int? McdPrimaryId { get; set; }
        public string Crid { get; set; }
        public string CapsId { get; set; }
        public bool? IsGovernment { get; set; }
        public string CityStateOfPo { get; set; }
        public string PoOfAccountZip { get; set; }
        public string MasterMid { get; set; }
        public string ChildMid { get; set; }
        public string IndiciaText { get; set; }
        public string LoginId { get; set; }
        public string PostageType { get; set; }
        public int? SiteId { get; set; }
    }
}

这是我的视图模型类;

using System.ComponentModel.DataAnnotations;

namespace API.ViewModels.EVS
{
    public class UserFeaturesEvsViewModel
    {
        public bool Active { get; set; }
        [Display(Name = "User ID")] 
        public int UserId { get; set; }

        [Display(Name = "Carrier ID")]
        [Required]
        public int CarrierId { get; set; }

        [Display(Name = "Site ID")]
        public int SiteId { get; set; }

        [Display(Name = "Nick Name")]
        public string LoginNickname { get; set; }

        [Display(Name = "Login Name")]
        [Required]
        public string CarrierLoginName { get; set; }

        [Display(Name = "Password")]
        [Required]
        public string CarrierLoginPassword { get; set; }

        [Display(Name = "Carrier Login ID")]
        public int CarrierLoginId { get; set; }

        [Display(Name = "MCD Primary ID")] 
        public int McdPrimaryId { get; set; }

        [Display(Name = "Permit Number")]
        public string PermitNumber { get; set; }

        [Display(Name = "Permit Type")]
        public string PermitType { get; set; }

        [Required]
        [Display(Name = "Permit Name")]
        public string PermitName { get; set; }

        [Display(Name = "CRID")]
        public string Crid { get; set; }

        [Display(Name = "CAPS ID")]
        public string EvsCapsId { get; set; }

        [Display(Name = "Government Permit")]
        public bool EvsGovernmentPermit { get; set; }

        [Display(Name = "PO of Account - City/State")]
        public string EvsCityStateOfPo { get; set; }

        [Display(Name = "PO of Account - Zipcode")]
        public string EvsPoOfAccountZip { get; set; }

        [Display(Name = "Master MID")
        public string EvsMasterMid { get; set; }

        [Display(Name = "Child ID")]
        public string EvsChildMid { get; set; }

        [Display(Name = "Indicia Text")]
        public string EvsIndiciaText { get; set; }

        [Display(Name = "Login ID")]
        public string EvsLoginId { get; set; }

        [Display(Name = "Postage Type")]
        public string PostageType { get; set; }

    }
}

这是我在API控制器类中创建的测试方法,用于从视图模型类手动映射到实体类

    private UserFeaturesEvs MapUserFeaturesEvsViewModelToUserFeaturesEvs(UserFeaturesEvsViewModel a_model)
    {
        try
        {
            UserFeaturesEvs model = new UserFeaturesEvs();
            if (a_model != null)
            {
                model.SiteId = a_model.SiteId;
                model.Active = a_model.Active;
                model.UserId = a_model.UserId;
                model.CarrierId = a_model.CarrierId;
                model.LoginNickname = a_model.LoginNickname;
                model.CarrierLoginName = a_model.CarrierLoginName;
                model.CarrierLoginPassword = a_model.CarrierLoginPassword;
                model.CarrierLoginId = a_model.CarrierLoginId;
                model.McdPrimaryId = a_model.McdPrimaryId;
                model.PermitNumber = a_model.PermitNumber;
                model.PermitType = a_model.PermitType;
                model.PermitName = a_model.PermitName;
                model.Crid = a_model.Crid;
                model.CapsId = a_model.EvsCapsId;
                model.IsGovernment = a_model.EvsGovernmentPermit;
                model.CityStateOfPo = a_model.EvsCityStateOfPo;
                model.PoOfAccountZip = a_model.EvsPoOfAccountZip;
                model.MasterMid = a_model.EvsMasterMid;
                model.ChildMid = a_model.EvsChildMid;
                model.IndiciaText = a_model.EvsIndiciaText;
                model.LoginId = a_model.EvsLoginId;
                model.PostageType = a_model.PostageType;
            }
            return model;

        }
        catch (Exception ex)
        {
            m_logger.LogError(1, ex, "An exception occurred in the MapUserFeaturesEvsViewModelToUserFeaturesEvs method while trying to map the UserFeaturesEvsViewModel [{VM}] to the UserFeaturesEvs Model.", a_model);
            return null;
        }

现在,通过使用F11的MapUserFeaturesEvsViewModelToUserFeaturesEvs方法,我可以看到在Visual Studio 2015中单步执行问题。一旦我跳过if(a_model!= null)行,调试只停止在SiteId,CarrierLoginId,McdPrimaryId和EvsGovernmentPermit的行上,跳过所有其他行。返回的UserFeaturesEvs对象仅包含这4个字段中的数据。我确认传递给方法的UserFeaturesEvsViewModel对象在所有属性中都有数据。

正在成功分配数据的UserFeatruesEvs类属性只是那些具有可空数据类型的数据?在数据类型之后,如下面的UserFeaturesEvs类的属性摘录所示;

        public int? CarrierLoginId { get; set; }
        public int? McdPrimaryId { get; set; }
        public bool? IsGovernment { get; set; }
        public int? SiteId { get; set; }

实体类UserFeaturesEvs是使用..

从现有数据库表生成的
dotnet ef dbcontext scaffold ... 

...命令。

上面的命令行命令还将下面的代码添加到DBContext类...

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<UserFeaturesEvs>(entity =>
        {
            entity.HasKey(e => e.UserId)
                .HasName("PK_UserFeatures_EVS");

            entity.ToTable("UserFeatures_EVS");

            entity.Property(e => e.UserId)
                .HasColumnName("UserID")
                .ValueGeneratedNever();

            entity.Property(e => e.CapsId)
                .HasColumnName("CapsID")
                .HasColumnType("varchar(50)");

            entity.Property(e => e.CarrierId).HasColumnName("CarrierID");

            entity.Property(e => e.CarrierLoginId).HasColumnName("CarrierLoginID");

            entity.Property(e => e.CarrierLoginName)
                .IsRequired()
                .HasColumnType("varchar(50)");

            entity.Property(e => e.CarrierLoginPassword)
                .IsRequired()
                .HasColumnType("varchar(50)");

            entity.Property(e => e.ChildMid)
                .HasColumnName("ChildMID")
                .HasColumnType("varchar(50)");

            entity.Property(e => e.CityStateOfPo)
                .HasColumnName("CityStateOfPO")
                .HasColumnType("varchar(50)");

            entity.Property(e => e.Crid)
                .HasColumnName("CRID")
                .HasColumnType("varchar(50)");

            entity.Property(e => e.IndiciaText).HasColumnType("varchar(50)");

            entity.Property(e => e.LoginId).HasColumnType("varchar(50)");

            entity.Property(e => e.LoginNickname).HasColumnType("varchar(50)");

            entity.Property(e => e.MasterMid)
                .HasColumnName("MasterMID")
                .HasColumnType("varchar(50)");

            entity.Property(e => e.McdPrimaryId).HasColumnName("MCDPrimaryID");

            entity.Property(e => e.PermitName).HasColumnType("varchar(50)");

            entity.Property(e => e.PermitNumber).HasColumnType("varchar(50)");

            entity.Property(e => e.PermitType).HasColumnType("varchar(50)");

            entity.Property(e => e.PoOfAccountZip)
                .HasColumnName("PoOfAccountZip")
                .HasColumnType("varchar(50)");

            entity.Property(e => e.PostageType).HasColumnType("varchar(50)");

            entity.Property(e => e.SiteId).HasColumnName("SiteID");
        });

    }

    public virtual DbSet<UserFeaturesEvs> UserFeaturesEvs { get; set; }

.
.
.

我看不出任何导致这个问题的东西,就像我之前说过的那样,我使用了同样的实体类/视图模型类技术和其他两个没有问题的数据库表。

我上面讨论的所有代码都在ASP.Net Core API项目中。

我试过删除?从UserFeaturesEvs类属性中的数据类型声明,当我这样做时,调试器只是跳过所有属性赋值并从映射方法返回所有空数据。

编辑:进一步分析表明问题出在UserFeaturesEvsViewModel上。我可以静态地将值分配给MapUserFeaturesEvsViewModelToUserFeaturesEvs方法中的UserFeaturesEvs对象实例,并且它不会跳过没有的属性赋值?在他们的数据类型声明之后。由于这是一个API,UserFeatruesEvsViewModel来自API控制器中的Create方法。

    [HttpPost("")]
    public IActionResult Create([FromBody]UserFeaturesEvsViewModel a_vm)
    {
        try
        {
            //Test assignment of vm to local variables
            int SiteId = a_vm.SiteId;   // This gets skipped
            bool Active = a_vm.Active;  // This gets skipped
            int UserId = a_vm.UserId;   // This gets skipped
            int CarrierId = a_vm.CarrierId; // This gets skipped
            string LoginNickname = a_vm.LoginNickname; // This gets skipped

            if (ModelState.IsValid)
            {
                UserFeaturesEvs model = MapUserFeaturesEvsViewModelToUserFeaturesEvs(a_vm);

                //UserFeaturesEvs model = Mapper.Map<UserFeaturesEvs>(a_vm);
                .
                .
                .

这是我通过Google Chrome Postman应用程序将API / json发布到API控制器的JSON示例

{
  "active": true,
  "userId": 1,
  "carrierId": 1,
  "siteId": 1,
  "loginNickname": "testNickname",
  "carrierLoginName": "testLoginName",
  "carrierLoginPassword": "testLoginPassword",
  "carrierLoginId": 0,
  "mcdPrimaryId": 0,
  "permitNumber": "123456",
  "permitType": "",
  "permitName": "UserID1-123456",
  "crid": "11",
  "evsCapsId": "112233",
  "evsGovernmentPermit": false,
  "evsCityStateOfPo": "Somewhere, NY",
  "evsPoOfAccountZip": "90210",
  "evsMasterMid": "654321",
  "evsChildMid": "654322",
  "evsIndiciaText": "Test",
  "evsLoginId": "",
  "postageType": "P"
}

当我在调试器中查看a_vm时,我看到我发布的数据,它看起来像是UserVeaturesEvsViewModel类的结构。但是,当我尝试在Create方法的开头简单地将a_vm变量的属性分配给局部变量时,如上所示,调试器也会跳过这些分配(即使我在分配上放置断点),就像MapUserFeaturesEvsViewModelToUserFeaturesEvs方法中发生的一样。

在我没有遇到此问题的其他API控制器中,我将a_vm对象传递到我的Entity Framework存储库中。例如,下面的代码在同一API的另一个控制器中工作正常。

    [HttpPost("")]
    public IActionResult Create([FromBody]MerSettingsViewModel a_vm)
    {
        try
        {
            if (ModelState.IsValid)
            {
                MailEventsRetrieverSettings settings = Mapper.Map<MailEventsRetrieverSettings>(a_vm);
                Task<MailEventsRetrieverSettings> task = m_cdiMppsRepository.AddMailEventsRetrieverSettingsAsync(settings);
                MailEventsRetrieverSettings response = task.Result;
                if (response != null)
                {
                    return Ok(response);
                }
            }
            return BadRequest(ModelState);
        }
        catch (Exception ex)
        {
            m_logger.LogError(1, ex, "Could not create new MailEventsRetrieverSettings record for the following object [{@VM}] to the database", a_vm);
            return Json(new { Message = ex.Message });
        }
    }

因此,问题似乎来自于在Create方法中从HttpPost的主体自动反序列化对象的方式。即使在调试器中,我看到a_vm中的值并且它看起来结构正确,如果类属性或局部变量未定义为,我无法直接将这些属性的值分配给另一个类属性或局部变量空。

有什么想法吗?

1 个答案:

答案 0 :(得分:0)

我对Automapper并不是很熟悉,但是阅读他们的wiki会说:

  

在将源映射到目标时,AutoMapper将忽略空引用异常。这是设计的。

您正在验证对象 - 尝试查看您传递的值是否为空。