EF Core 6 Update Data direct from an Object, n-n, n-1, 1-1

Samuel Hurni 1 Reputation point
2022-06-20T16:24:44.86+00:00

Hello together

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; }  
  
  
}  

And here 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?

Entity Framework Core
Entity Framework Core
A lightweight, extensible, open-source, and cross-platform version of the Entity Framework data access technology.
698 questions
{count} votes