简体   繁体   English

使用外键发布新实体会导致创建另一个外部实体而不是引用现有实体

[英]Posting a new entity with foreign key causes creating another foreign entity instead of referencing existing one

I have a multi-layered onion architectured asp.net application and currently fasing an issue with POSTing a new entity in a table, which has one-to-many foreign key relation with another table.我有一个多层洋葱架构的 asp.net 应用程序,目前正在解决在表中发布新实体的问题,该表与另一个表具有一对多的外键关系。

Here is my parent entity:这是我的父实体:

    public class Feature : Entity
{
    public string Name { get; set; }
    public decimal Price { get; set; }
    public FeatureType FeatureType { get; set; }
    public ICollection<ConfigurationFeatureState> Configurations { get; set; }
}

and here is a referenced one:这是一个参考:

public class FeatureType: Entity
{
    public string Name { get; set; }
    public bool IsOptional { get; set; }
    public ICollection<Feature> Features { get; set; }
}

Entity model just adds an ID to them实体 model 只是为它们添加了一个 ID

public abstract class Entity 
{
    public Guid ID { get; private set; } = Guid.NewGuid();
}

So, a Feature may have a single FeatureType only, but FeatureType has many Features.因此,一个 Feature 可能只有一个 FeatureType,但 FeatureType 有很多 Feature。

I'm using FeatureDto for a presentation, which is being mapped to Feature in FeatureService.我正在使用 FeatureDto 进行演示,它被映射到 FeatureService 中的 Feature。

Here is a FeatureDto, which just shows a GUID FeatureTypeID insted of FeatureType entity:这是一个 FeatureDto,它只显示一个 GUID FeatureTypeID insted FeatureType 实体:

public class FeatureDto : BaseDto
{
    public string Name { get; set; }
    public decimal Price { get; set; }
    public Guid FeatureTypeID { get; set; }
    
}

Here is my FeatureController POST method:这是我的 FeatureController POST 方法:

[HttpPost]
public async Task<IActionResult> Post([FromBody] FeatureDto newFeatureDto)
{
    var featureDto = await _featureService.CreateAsync(newFeatureDto);
    return CreatedAtRoute("FeatureById", new { id = featureDto.ID }, featureDto);
}

And here is a CreateAsync method from FeatureService, which is called from a generic base class (TDto = FeatureDto, TEntity = Feature):这是来自 FeatureService 的 CreateAsync 方法,它是从通用基础 class 调用的(TDto = FeatureDto,TEntity = Feature):

public async Task<TDto> CreateAsync(TDto newEntityDto)
{
    var entity = Mapper.Map<TEntity>(newEntityDto);
    _repository.Create(entity);
    await UnitOfWork.SaveAsync();

    var dto = Mapper.Map<TDto>(entity);
    return dto;
}

I'm using AutoMapper to map Feature to FeatureDto and vice versa using the following map:我使用AutoMapper到 map Feature 到 FeatureDto,反之亦然,使用以下 map:

CreateMap<Feature, FeatureDto>()
    .ForMember(dto => dto.FeatureTypeID, opt => opt.MapFrom(db => db.FeatureType.ID))
    .ReverseMap();

And here is the problem: whenever I try to POST a new Feature into the db referring to already existing FeatureType, another empty FeatureType is being created (which I don't need).这就是问题所在:每当我尝试将新功能发布到数据库中并引用已经存在的功能类型时,就会创建另一个空的功能类型(我不需要)。

Here is the example:这是示例:

I'm posting a new Feature via Postman with this request body:我正在通过 Postman 使用此请求正文发布新功能:

{
    "name": "LED Strip Neon Car Bottom",
    "price": 200.00,
    "featureTypeID": "41b737f9-1649-4a66-94c7-065d099408e6" // value with this ID is already present in FeatureType table
}

and getting the error Microsoft.Data.SqlClient.SqlException (0x80131904): Cannot insert the value NULL into column 'Name', table 'DB.dbo.并收到错误Microsoft.Data.SqlClient.SqlException (0x80131904):无法将值 NULL 插入列“名称”、表“DB.dbo”。 FeatureTypes ';特征类型'; column does not allow nulls.列不允许空值。 INSERT fails.插入失败。 The statement has been terminated.该语句已终止。

This is because during the CreateAsync method a new FeatureType is being created along with a Feature: CreateAsync method debugging .这是因为在 CreateAsync 方法期间,正在创建一个新的 FeatureType 以及一个 Feature: CreateAsync method debugging

Why is this happening and how can I assure that instead of inserting a new FeatureType, the already existing one will be selected from the db?为什么会发生这种情况,我如何确保不会插入新的 FeatureType,而是从数据库中选择已经存在的 FeatureType?

I have configured all the relations using Fluent API and they were set up properly in the DB, so that the Foreign Key is present there: feature table in db我已经使用 Fluent API 配置了所有关系,并且它们在数据库中设置正确,因此外键存在于那里:数据库中的特征表

Here is the configuration class as well, if you need it:这里也是配置 class,如果你需要的话:

class FeatureEntityConfiguration : IEntityTypeConfiguration<Feature>
{
    public void Configure(EntityTypeBuilder<Feature> builder)
    {
        builder.HasOne(f => f.FeatureType).WithMany(ft => ft.Features).IsRequired().HasForeignKey("FeatureTypeID");

        builder.HasKey(f => f.ID);

        builder.Property(f => f.Name).IsRequired().HasMaxLength(100);

        builder.Property(f => f.Price).HasColumnType("decimal(18,2)");
    }
}

Sorry if that's a weird question, but I'm quite a newbie with this stuff.抱歉,如果这是一个奇怪的问题,但我对这些东西还是个新手。 Thank you in advance!先感谢您!

I'm not sure in all your layers where this goes, but you just need to set the Navigation Property (possibly a Shadow Property in EF Core), for the FeatureTypeId, or explicitly mark the FeatureType as Unchanged, after (or in)我不确定在你所有的层中这会发生什么,但你只需要为 FeatureTypeId 设置导航属性(可能是 EF Core 中的阴影属性),或者在之后(或之中)将 FeatureType 显式标记为未更改

_repository.Create(entity);

Have you tried to get the feature type and manually assign your navigation property?您是否尝试获取功能类型并手动分配您的导航属性?

public async Task<TDto> CreateAsync(TDto newEntityDto)
{
    var entity = Mapper.Map<TEntity>(newEntityDto);

    /***/
    var featureType = _featureTypeRepository.Get(newEntityDto.featureTypeID);
    entity.FeatureType = featureType;
    /***/ 

    _repository.Create(entity);
    await UnitOfWork.SaveAsync();

    var dto = Mapper.Map<TDto>(entity);
    return dto;
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM