EF Core Update with tracked objects

aspnetboilerplate c# entity-framework-core


I use ABP Framework and I am really struggling on an update issue. I use entity frmaework core.

I am gonna try to ba as clear as I can.

I have a category table which hosts category names and details of the category.

Then I am building an angular UI which allows to select categories and store them in an accomplishement object.

Then my goal is to store Accomplishement and categories.

I then created a table AccomplishementCategoryCustoms.

So my model is as below:

public class Accomplishment : AuditedEntity 

    public virtual string Name { get; set; }

   public List<AccomplishementCategoryCustom> Categories { get; set; }

public class AccomplishementCategoryCustom : CreationAuditedEntity 

    public virtual int? CategoryId { get; set; }

    public Category CategoryFk { get; set; }

    public virtual int? AccomplishmentId { get; set; }

    public Accomplishment AccomplishmentFk { get; set; }


I do not have relations between categories and AccomplishementCategoryCustoms.

Then when I insert it works well.

When I update and add the include I have the error as below:

With include

When I update without include I have an error:

Microsoft.EntityFrameworkCore.DbUpdateException: An error occurred while updating the entries. See the inner exception for details. ---> System.Data.SqlClient.SqlException: Cannot insert explicit value for identity column in table 'AccomplishementCategoryCustoms' when IDENTITY_INSERT is set to OFF.

FInally the modelbuilder is as below:

            name: "AccomplishementCategoryCustoms",
            columns: table => new
                Id = table.Column<int>(nullable: false)
                    .Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
                CreationTime = table.Column<DateTime>(nullable: false),
                CreatorUserId = table.Column<long>(nullable: true),
                CategoryId = table.Column<int>(nullable: true),
                AccomplishmentId = table.Column<int>(nullable: true)
            constraints: table =>
                table.PrimaryKey("PK_AccomplishementCategoryCustoms", x => x.Id);
                    name: "FK_AccomplishementCategoryCustoms_Accomplishments_AccomplishmentId",
                    column: x => x.AccomplishmentId,
                    principalTable: "Accomplishments",
                    principalColumn: "Id",
                    onDelete: ReferentialAction.Restrict);
                    name: "FK_AccomplishementCategoryCustoms_Categories_CategoryId",
                    column: x => x.CategoryId,
                    principalTable: "Categories",
                    principalColumn: "Id",
                    onDelete: ReferentialAction.Restrict);

            name: "IX_AccomplishementCategoryCustoms_AccomplishmentId",
            table: "AccomplishementCategoryCustoms",
            column: "AccomplishmentId");

            name: "IX_AccomplishementCategoryCustoms_CategoryId",
            table: "AccomplishementCategoryCustoms",
            column: "CategoryId");

I hope it will be enough detailed to have the fix because I am struggled on it since a long time and can't sort it out.

10/31/2019 11:40:25 PM

Popular Answer

Based on your answer, assuming _dbContextProvider.GetDbContext(Abp.MultiTenancy.MultiTenancySides.Host) returns the database context you are targeting, which I have inferred from your answer, you can use the following code to persist changes to an accomplishment, including adding or removing categories, assuming as well that ObjectMapper.Map(input, accomplishment) is what is making changes to the accomplishment received from the repository, using the input, also assuming that ObjectMapper.Map(input, accomplishment) returns an entity type.

async Task Update(CreateOrEditAccomplishmentsDto input)
    var context = _dbContextProvider.GetDbContext(Abp.MultiTenancy.MultiTenancySides.Host);
    context.Entry(ObjectMapper.Map(input, _accomplishmentRepository.GetAll().Where(a => a.Id == input.Id.Value).Include(a => a.Categories).FirstOrDefault())).State = EntityState.Modified;
    await context.SaveChangesAsync();

Technically, the context.Entry(...).State = EntityState.Modified part of that line is redundant, but I'm not sure as to the implementation of anything you have provided, so I'm accounting for the context not actually tracking the returned object from the call to Map.

If you are getting an exception due to the context already tracking an entity with the same value for id, then remove the following line.

context.Entry(ObjectMapper.Map(input, _accomplishmentRepository.GetAll().Where(a => a.Id == input.Id.Value).Include(a => a.Categories).FirstOrDefault())).State = EntityState.Modified;

Afterwards, add the following line in it's place before the call to SaveChangesAsync.

ObjectMapper.Map(input, _accomplishmentRepository.GetAll().Where(a => a.Id == input.Id.Value).Include(a => a.Categories).FirstOrDefault());
11/2/2019 5:08:37 PM

Related Questions


Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow