Status Update (12/19/18)
The problem is that when Map<Product>(cmd)
executes, AutoMapper maps a new Color
type (with null values) to Product.
Question:
Could this be a bug when AutoMapper tries to “flatten’ the ColorSystemName
member?
ProductCmd.cs
public class ProductCmd
{
public virtual int? ColorId { get; set; }
public virtual ColorCmd Color { get; set; }
public virtual string ColorSystemName { get; set; }
}
I found this by removing the Color
navigation property from Product and ProductCmd and executing AssertConfigurationIsValid
:
Startup.cs
public class Startup
{
public void Configure(IApplicationBuilder app,
IHostingEnvironment env,
IMapper autoMapper)
{
autoMapper.ConfigurationProvider.AssertConfigurationIsValid();
}
}
Now, I get:
AutoMapper.AutoMapperConfigurationException
HResult=0x80131500
Source=AutoMapper
StackTrace: Cannot evaluate the exception stack trace‘AutoMapperConfigurationException: Unmapped members were found’.
This problem does not occur when I remove the ColorSystemName
member:
ProductCmd.cs
public class ProductCmd
{
public virtual int? ColorId { get; set; }
public virtual ColorCmd Color { get; set; }
}
Question:
Did AutoMapper instantiate a new Color type while trying to flatten ColorSystemName
in the original configuration which contained a null Color
navigation property?
Original Post
I want AutoMapper to map a null (EF Core navigation) property Color
from ProductCmd
to Product
.
I have tried using a single navigation property as well as a fully-defined relationship (including an inverse navigation property).
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();
}
}
}
Libraries utilized:
- 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
Problem also occurs with:
- AutoMapper.Extensions.Microsoft.DependencyInjection v.5.0.1
- AutoMapper v.7.0.1
I could not find a solution to this scenario in AutoMapper Documentation or various online resources.
Here is sample code showing the mapping results and error:
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."
//
}
}
User contributions licensed under CC BY-SA 3.0