Search code examples
c#entity-frameworkentity-framework-6automapperautomapper-6

C# Entity Framework 6 - AutoMapper 6 - Collections/List - Add and Update works but removing item throws an error


As the title says I can add and update but when it comes to deleting I get an error.

The operation failed: The relationship could not be changed because one or more of the foreign-key properties is non-nullable. When a change is made to a relationship, the related foreign-key property is set to a null value. If the foreign-key does not support null values, a new relationship must be defined, the foreign-key property must be assigned another non-null value, or the unrelated object must be deleted.

I understand that the Description in this case only gets a null foreign key but is never deleted. I have seen some examples where they suggest looping through every child item and delete them one by one. Imao I think there should be a better way. What I'm looking for is a solution with minimal impact and just tell EF to delete the entire item and not only null the foreign key.

https://stackoverflow.com/a/5540956/3850405

Using AutoMapper, AutoMapper.Collection and AutoMapper.Collection.EntityFramework.

Controller method:

public async Task<IHttpActionResult> UpdateArticle(ArticleViewModel articleVm)
{
    Article articleOriginal = await iArticleRepository.GetAsync(articleVm.Id);
    Article updatedArticle = Mapper.Map<ArticleViewModel, Article>(articleVm, articleOriginal);
    await iArticleRepository.UpdateAsync(updatedArticle);
    return Ok();
}

Mapping:

Mapper.Initialize(cfg =>
{
    cfg.AddCollectionMappers();

    cfg.SetGeneratePropertyMaps<GenerateEntityFrameworkPrimaryKeyPropertyMaps<DbContext>>();

    cfg.CreateMap<ArticleViewModel, Article>(MemberList.Source)
        .EqualityComparison((src, dst) => src.Id == dst.Id);

    cfg.CreateMap<DescriptionViewModel, Description>(MemberList.Source)
        .EqualityComparison((src, dst) => src.Id == dst.Id);
}
Mapper.AssertConfigurationIsValid();

Viewmodels:

public class ArticleViewModel
{
    public int Id { get; set; }

    public string Name { get; set; }

    public List<DescriptionViewModel> Descriptions { get; set; }
}

public class DescriptionViewModel
{
    public int Id { get; set; }

    public string Heading { get; set; }
}

Models:

public class Article : IEntity<int>
{
    public Article()
    {
        Descriptions = new List<Description>();
    }

    [Key]
    public int Id { get; set; }

    public DateTime Created { get; set; }

    public DateTime Updated { get; set; }

    [MaxLength(256)]
    public string Name { get; set; }

    public virtual ICollection<Description> Descriptions { get; set; }
}

public class Description: IEntity<int>
{
    [Key]
    public int Id { get; set; }

    public DateTime Created { get; set; }

    public DateTime Updated { get; set; }

    [MaxLength(256)]
    public string Heading { get; set; }

    public int ArticleId { get; set; }

    public virtual Article Article { get; set; }
}

Solution

  • Got a solution from this answer

    https://stackoverflow.com/a/32983252/3850405

    and this blog:

    http://www.kianryan.co.uk/2013/03/orphaned-child/

    Code:

    public class Description: IEntity<int>
    {
        [Key, Column(Order = 0), DatabaseGenerated(DatabaseGeneratedOption.Identity)]
        public int Id { get; set; }
    
        public DateTime Created { get; set; }
    
        public DateTime Updated { get; set; }
    
        [MaxLength(256)]
        public string Heading { get; set; }
    
        [Key, Column(Order = 1)]
        public int ArticleId { get; set; }
    
        public virtual Article Article { get; set; }
    }
    

    I can really recommend reading Mosh's answer about the difference between composition and aggregation since it will help you understand EF better.