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.
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
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.
I’m using FeatureDto for a presentation, which is being mapped to Feature in FeatureService.
Here is a FeatureDto, which just shows a GUID FeatureTypeID insted of FeatureType entity:
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:
[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):
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:
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:
{
"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.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 .
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?
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
Here is the configuration class as well, if you need it:
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!
2
Answers
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)
Have you tried to get the feature type and manually assign your navigation property?