我有一个非常奇怪的问题。我正在创建一个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中的值并且它看起来结构正确,如果类属性或局部变量未定义为,我无法直接将这些属性的值分配给另一个类属性或局部变量空。
有什么想法吗?
答案 0 :(得分:0)
我对Automapper并不是很熟悉,但是阅读他们的wiki会说:
在将源映射到目标时,AutoMapper将忽略空引用异常。这是设计的。
您正在验证对象 - 尝试查看您传递的值是否为空。