状态更新(12/19/18)
问题在于执行Map<Product>(cmd)
时,AutoMapper将新的Color
类型(具有空值)映射到产品。
问题:
当AutoMapper尝试“拉平” ColorSystemName
成员时,这可能是一个错误吗?
ProductCmd.cs
public class ProductCmd
{
public virtual int? ColorId { get; set; }
public virtual ColorCmd Color { get; set; }
public virtual string ColorSystemName { get; set; }
}
我发现这是通过从 Product 和 ProductCmd 中删除Color
导航属性并执行AssertConfigurationIsValid
:
Startup.cs
public class Startup
{
public void Configure(IApplicationBuilder app,
IHostingEnvironment env,
IMapper autoMapper)
{
autoMapper.ConfigurationProvider.AssertConfigurationIsValid();
}
}
现在,我得到了:
AutoMapper.AutoMapperConfigurationException
HResult = 0x80131500
来源= AutoMapper
StackTrace:无法评估异常堆栈跟踪‘AutoMapperConfigurationException:未映射的成员是 找到”。
当我删除ColorSystemName
成员时,不会发生此问题:
ProductCmd.cs
public class ProductCmd
{
public virtual int? ColorId { get; set; }
public virtual ColorCmd Color { get; set; }
}
问题:
尝试在包含空ColorSystemName
导航属性的原始配置中展平Color
时,AutoMapper是否实例化了新的 Color 类型?
原始帖子
我希望AutoMapper将空(EF核心导航)属性Color
从ProductCmd
映射到Product
。
我尝试使用单个导航属性以及完全定义的关系(包括反向导航属性)。
Product.cs
public class Product
{
public virtual int? ColorId { get; set; }
public virtual Color Color { get; set; }
}
ProductCmd.cs
public class ProductCmd
{
public virtual int? ColorId { get; set; }
public virtual ColorCmd Color { get; set; }
public virtual string ColorSystemName { get; set; }
}
Color.cs
public class Color
{
public virtual int Id { get; set; }
public virtual string SystemName { get; set; }
}
ColorCmd.cs
public class ColorCmd
{
public virtual int Id { get; set; }
public virtual string SystemName { get; set; }
}
ProductTypeConfiguration.cs
public class ProductTypeConfiguration : IEntityTypeConfiguration<Product>
{
public void Configure(EntityTypeBuilder<Product> builder)
{
builder.ToTable("Product", "dbo");
builder.HasKey(a => a.Id);
builder.HasOne(a => a.Color)
.WithMany()
.HasForeignKey(s => s.ColorId)
.OnDelete(DeleteBehavior.ClientSetNull)
.IsRequired(false);
}
}
ColorTypeConfiguration.cs
public class ColorTypeConfiguration : IEntityTypeConfiguration<Color>
{
public void Configure(EntityTypeBuilder<Color> builder)
{
builder.ToTable("Color", "dbo");
builder.HasKey(a => a.Id);
}
}
InitialInventoryDbMigration.cs
public partial class InitialInventoryDbMigration : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "Color",
schema: "dbo",
columns: table => new
{
Id = table.Column<int>(nullable: false)
.Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
SystemName = table.Column<string>(maxLength: 256, nullable: false),
},
constraints: table =>
{
table.PrimaryKey("PK_Color", x => x.Id);
});
migrationBuilder.CreateTable(
name: " Product",
schema: "dbo",
columns: table => new
{
Id = table.Column<int>(nullable: false)
.Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
ColorId = table.Column<int>(nullable: true),
},
constraints: table =>
{
table.PrimaryKey("PK_ Product", x => x.Id);
table.ForeignKey(
name: "FK_Product_Color_ColorId",
column: x => x.ColorId,
principalSchema: "dbo",
principalTable: " Color",
principalColumn: "Id",
onDelete: ReferentialAction.Restrict);
});
}
Startup.cs
public class Startup
{
public IServiceProvider ConfigureServices(IServiceCollection services)
{
_services.AddAutoMapper(cfg =>
{
cfg.AllowNullDestinationValues = true;
},
new[]
typeof(AutoMapperProfiles_Inventory)
});
}
}
AutoMapperProfiles_Inventory.cs
public class AutoMapperProfiles_Inventory
{
public List<Profile> GetProfiles()
{
var profiles = new List<Profile>()
{
new AutoMapperProfile_Inventory_Cmd()
};
return profiles;
}
}
AutoMapperProfile_Inventory_Cmd.cs
using features = Module.Inventory.ApplicationCore.BLL.Domain.Features;
using pocos = Module.Inventory.ApplicationCore.BLL.Domain.Entities;
using AutoMapper;
namespace Dcs.NetCore.Module.Inventory.ApplicationCore.BLL.Domain.Entities.Mappers.Profiles
{
public class AutoMapperProfile_Inventory_Cmd : Profile
{
public AutoMapperProfile_Inventory_Cmd()
{
CreateMap<pocos.Color, features.Colors.Cmd>().ReverseMap();
CreateMap<pocos.Product, features.Products.Cmd>()
.ReverseMap();
}
}
}
使用的图书馆:
- Microsoft.EntityFrameworkCore v.2.1.4
- Microsoft.NETCore.App v.2.1
- AutoMapper.Extensions.Microsoft.DependencyInjection v.6.0.0
- AutoMapper v.8.0.0
问题还出现在:
- AutoMapper.Extensions.Microsoft.DependencyInjection v.5.0.1
- AutoMapper v.7.0.1
在AutoMapper Documentation或各种在线资源中找不到解决此问题的方法。
以下是显示映射结果和错误的示例代码:
public class Example
{
private readonly IMapper _mapper;
private readonly IProductRepository _repository;
public Example(IMapper mapper, IProductRepository repository)
{
_mapper = mapper;
_repository = repository;
}
public void Add()
{
var cmd = new Cmd()
{
Color = null,
ColorId = null,
Id = 0,
ProductBrand = null,
ProductBrandId = 2
};
var dao = _mapper.Map<Product>(cmd);
//
//
// at this point:
//
//
// dao.Color == Color(with null values)
// dao.Color.Id == 0
// dao.ColorId == null
// dao.Id == 0
// dao.ProductBrand == ProductBrand(with null values)
// dao.ProductBrand.Id == 0
// dao.ProductBrandId == 1
//
//
_repository.AddAsync(dao);
//
// executes dbSet.Add(dao);
//
//
// at this point:
//
// dao.Color == Color(with null values)
// dao.Color.Id == -2147482643
// dao.ColorId == -2147482643
// dao.Color.Products[0].Color.Id == -2147482643
// dao.Color.Products[0].Id == -2147482647
// dao.Id == -2147482647
// dao.ProductBrand == ProductBrand(with correct values)
// dao.ProductBrand.Id == 1
// dao.ProductBrandId == 1
//
//
_repository.SaveChangesAsync();
//
// executes _dbContext.SaveChangesAsync();
//
//
// at this point:
//
// System.Data.SqlClient.SqlException(0x80131904):
// "Cannot insert the value NULL into column 'AddedBy',
// table 'dbo.Color';
// column does not allow nulls.
// INSERT fails.\r\nThe statement has been terminated."
//
}
}