0

I have some trouble to add a simple update Endpoint to my asp.Net API where I want to update an Entity and also his relations.

I already tried a lot of ways to update the data and the relations in one step, but I had different errors, like tracking error, duplicate key in joining table or no error but the relation was not updated.

So first I tried to update my data using the connected State with the tracking of EF Core. Normaly I want to use the DTO Pattenr with Automapper but to keep it simple I have an example without this step between.

Here is my model configuration of product:

public class Product
{
    [Key]
    public int ProductId { get; set; }
    [Required]
    public string Name { get; set; }
    [Required]
    public string Description { get; set; }
    [Required]
    public double Price { get; set; }

    public SubCategorie SubCategorie { get; set; }              //Relation n-1 to Table Subcategorie
    
    public ICollection<Extra> Extra { get; set; }               //Relation n-n to Table Extra
    

    public DateTime CreatedDate { get; set; } = DateTime.Now;
    public string CreatedBy { get; set; }
    public string UpdatedBy { get; set; }
    public DateTime UpdatedDate { get; set; }


}

Andher the config of the extra model which is in n-n relation to the product model

public class Extra
{
    [Key]
    public int ExtraId { get; set; }
    [Required]
    public string Name { get; set; }
    [Required]
    public string Tooltip { get; set; }
    [Required]
    public double Price { get; set; }

    public ICollection<Product> Products { get; set; }                  //Relation n-n to Table Product

    public ICollection<ProductConfig> ProductConfigs { get; set; }      //Relation n-n to Table PrductConfig

    public DateTime CreatedDate { get; set; } = DateTime.Now;
    public string CreatedBy { get; set; }
    public string UpdatedBy { get; set; }
    public DateTime UpdatedDate { get; set; }


}

and here the onModelCreatin Method in DBContex, where I configure the relations

      // Product           
        // n-n to Extra
        modelBuilder.Entity<Product>()
           .HasMany<Extra>(e => e.Extra)
           .WithMany(e => e.Products);
        //n-n to Type
        modelBuilder.Entity<Product>()
           .HasMany<ProductType>(e => e.ProductType)
           .WithMany(e => e.Products);
        // n-1 to Subcategorie
        modelBuilder.Entity<Product>()
         .HasOne<SubCategorie>(e => e.SubCategorie)
         .WithMany(e => e.Product);
        // 1-n to productconfig
        modelBuilder.Entity<Product>()
        .HasMany<ProductConfig>(e => e.ProductConfigs)
        .WithOne(e => e.Product);

Here is the example with the connected state. The problem is here that nothing at the relations happened, for example when an extra object is added and comes with the "porductNew" input.

 public async Task<Product> UpdateProduct(int productId, Product productNew)
    {
        try
        {
            if (productId == productNew.ProductId)
            {
                //valid
                Product product = await _db.Product.AsNoTracking()
                .Include(x => x.Extra)
                .Include(x => x.ProductType)
                .FirstOrDefaultAsync(x => x.ProductId == productId);                    
                product = productNew;                  
                await _db.SaveChangesAsync();
                return product;
            }
            else
            {
                //invalid
                throw new Exception("Id for updating and Id in the Data not match");
            }
        }
        catch (Exception ex)
        {
            throw new Exception(ex.ToString());
        }
    }

When I set the entity State manually to modified I have a tracking error "the istnace could not been tracked because another instance with the same keyvalue..."

So I tried also with the disconnected state schema. For this I use the update Method. There the relations will be added correct, but when i commit the same changes again, EF Core set the state inside the joining table to added than modified

Here is the repository for this pattern:

public async Task<Product> UpdateProduct(int productId, Product productNew)
    {
        try
        {
            if (productId == productNew.ProductId)
            {
                //valid
                Product product = await _db.Product.AsNoTracking()
                .Include(x => x.SideDishs)
                .Include(x => x.Extra)
                .Include(x => x.ProductType)
                .FirstOrDefaultAsync(x => x.ProductId == productId);
               
                product = productNew;
               
                var updatedProduct = _db.Product.Update(product);
              
                await _db.SaveChangesAsync();
                return product;
            }
            else
            {
                //invalid
                throw new Exception("Id for updating and Id in the Data not match");
            }
        }
        catch (Exception ex)
        {
            throw new Exception(ex.ToString());
        }
    }

Here is the changetracker from EF Core debugging. The Joining table should in this case have the state modified than added because there was no change at the relations. The error of duplicate key in joining table happens:

Extra {ExtraId: 1} Modified

ExtraId: 1 PK

CreatedBy: 'Admin' Modified

CreatedDate: '19.06.2022 09:54:16' Modified

Name: 'zwei Teller' Modified

Price: 0 Modified

Tooltip: 'Das Essen wird auf zwei Teller serviert' Modified

UpdatedBy: Modified

UpdatedDate: '01.01.0001 00:00:00' Modified

ProductConfigs:

Products: [{ProductId: 1}]

Extra {ExtraId: 2} Modified

ExtraId: 2 PK

CreatedBy: 'Admin' Modified

CreatedDate: '19.06.2022 09:55:27' Modified

Name: 'extra scharf' Modified

Price: 0 Modified

Tooltip: 'Gewürzt mit spezieller Chili Paste' Modified

UpdatedBy: Modified

UpdatedDate: '01.01.0001 00:00:00' Modified

ProductConfigs:

Products: [{ProductId: 1}]

ExtraProduct {ExtraId: 1, ProductsProductId: 1} Added

ExtraId: 1 PK FK

ProductsProductId: 1 PK FK

ExtraProduct {ExtraId: 2, ProductsProductId: 1} Added

ExtraId: 2 PK FK

ProductsProductId: 1 PK FK

Product {ProductId: 1} Modified

ProductId: 1 PK

CreatedBy: 'Admin' Modified

CreatedDate: '19.06.2022 10:27:17' Modified

Description: 'Beschreibung Testprodukt' Modified

Name: 'Testprodukt 1' Modified

Price: 15 Modified

SubCategorieId: FK Unknown

UpdatedBy: 'string' Modified

UpdatedDate: '19.06.2022 08:26:45' Modified

ProductConfigs:

SubCategorie:

Extra: [{ExtraId: 1}, {ExtraId: 2}]

ProductType: []

So does anybody have a simple idea or know a documentation, where the update sequence for ef-core6 with all types of relations with external objects for updating is described?

1 Answer 1

0

When you are finding the data and the data will be modified, you should not use the AsNoTracking() method, please read more about Tracking vs. No-Tracking Queries.

public async Task<Product> UpdateProduct(int productId, Product productNew)
{
    try
    {
        Product product = await _db.Product
                                .Include(x => x.SideDishs)
                                .Include(x => x.Extra)
                                .Include(x => x.ProductType)
                                .FirstOrDefaultAsync(x => x.ProductId == productId);
        if (productId == productNew.ProductId && product != null)
        {
            //valid
            product = productNew;
            await _db.SaveChangesAsync();
            return product;
        }
        else
        {
            //invalid
            throw new Exception("Id for updating and Id in the Data not match");
        }
    }
    catch (Exception ex)
    {
        throw new Exception(ex.ToString());
    }
}
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.