AutoMapper + обнуляемый внешний ключ + Entity Framework Core - PullRequest
0 голосов
/ 01 декабря 2018

Обновление статуса (19.12.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; }
}

Я нашел это, удалив свойство навигации Color из Product и ProductCmd и выполнивAssertConfigurationIsValid:

Startup.cs

public class Startup
{
    public void Configure(IApplicationBuilder app, 
                          IHostingEnvironment env, 
                          IMapper autoMapper)
    {
        autoMapper.ConfigurationProvider.AssertConfigurationIsValid();
    }
}

Теперь я получаю:

AutoMapper.AutoMapperConfigurationException
HResult =0x80131500
Source = AutoMapper
StackTrace: Невозможно оценить трассировку стека исключений

'AutoMapperConfigurationException: Обнаружены несопоставленные элементы'.

Эта проблема не возникает при удаленииColorSystemName член:

ProductCmd.cs

public class ProductCmd
{
    public virtual int? ColorId { get; set; }
    public virtual ColorCmd Color { get; set; }
}

Вопрос:
Создал ли AutoMapper новый тип Color при попытке сгладить ColorSystemName в исходной конфигурации, содержащей нулевое Color свойство навигации?

Исходное сообщение

Я хочу, чтобы AutoMapper отображал нулевое свойство (навигация EF Core) 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

Я не смог найти решение этого сценария, яn Документация AutoMapper или различные онлайн-ресурсы.

Вот пример кода, показывающий результаты сопоставления и ошибку:

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."            
        //
    }

}

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...