EF Core - Update w relacji wiele do wielu

0

Cześć.
Mam kłopot z aktualizacją rekordu w relacji wiele do wielu. Moje obiekty to:
Product, Meal oraz MealProduct. Jeden posiłek może zawierać wiele produktów, problem pojawił się w momencie, gdy chciałem zaktualizować rekord, kiedy to użytkownik nie dodał żadnego posiłku dla danego produktu - tabela w MealProduct była pusta. Następnie chcąc dodać produkty do posiłku zwróciło mi ex:

Microsoft.Data.SqlClient.SqlException (0x80131904): Cannot update identity column 'Id'.
   at Microsoft.Data.SqlClient.SqlCommand.<>c.<ExecuteDbDataReaderAsync>b__169_0(Task`1 result)
   at System.Threading.Tasks.ContinuationResultTaskFromResultTask`2.InnerInvoke()
   at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state)

Dodam, że moim PK w tabeli MealProduct jest ProductId oraz MealId. Poniżej konfig:

    public class MealProductMap
    {
        public MealProductMap(EntityTypeBuilder<MealProduct> entityBuilder)
        {
            entityBuilder
                .HasKey(t => new { t.MealId, t.ProductId });
            entityBuilder
                .HasOne(t => t.Meal)
                .WithMany(a => a.MealProducts)
                .HasForeignKey(t => t.MealId)
                .OnDelete(Microsoft.EntityFrameworkCore.DeleteBehavior.Cascade);
            entityBuilder
                .HasOne(t => t.Product)
                .WithMany(a => a.MealProducts)
                .HasForeignKey(t => t.ProductId)
                .OnDelete(Microsoft.EntityFrameworkCore.DeleteBehavior.Cascade);
        }
    }

Poniżej też część zapytania, które wygenerował EF CORE i zamierzał wykonać:

UPDATE [MealProducts] SET [CanBeEdited] = @p0, [CreationDate] = @p1, [DeleteDate] = @p2, [Id] = @p3, [IsDeleted] = @p4, [Quantity] = @p5
WHERE [MealId] = @p6 AND [ProductId] = @p7;
SELECT [ModifyDate]
FROM [MealProducts]
0

Wstaw kod modeli i kod odpowiedzialny za aktualizacje rekordu. [Id] = @p3 dziwnie wygląda.

0

@Kokoniłaj:

    public class Meal : _BaseEntity
    {
        public TypesOfMeal TypeOfMeal { get; set; }
        public string MealName { get; set; }
        public string Description { get; set; }
        public string UserId { get; set; }

        public int Kcal { get; set; }
        [ForeignKey("MealId")]
        public virtual List<MealProduct> MealProducts { get; set; } = new List<MealProduct>();
        public virtual List<DietDayMeals> DayDietMeals { get; set; }
    }
    public class MealProduct : _BaseEntity
    {
        [Key]
        [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
        public int Id { get; set; }
        public int? Quantity { get; set; }
        public Guid MealId { get; set; }
        public Meal Meal { get; set; }
        public Guid ProductId { get; set; }
        public Product Product { get; set; }
    }
    public class Product : _BaseEntity
    {
        public static Product GetProductFromDTO(ProductDTO dto)
        {
            return new Product()
            {
                Id = dto.Id,
                Category = dto.Category,
                Kcal = dto.Kcal,
                PhotoPath = dto.PhotoPath,
                ProductName = dto.ProductName,
                QuantityUnit = dto.QuantityUnit,
                Unit = dto.Unit
            };
        }
        public string ProductName { get; set; }
        public int Kcal { get; set; }
        public int QuantityUnit { get; set; }
        public Unit Unit { get; set; }
        public ProductCategories Category { get; set; }
        public virtual List<MealProduct> MealProducts { get; set; }
        public string PhotoPath { get; set; }
    }

Base Entity:

public abstract class _BaseEntity
    {
        public _BaseEntity() { }

        [Key]
        [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
        public Guid Id { get; set; }

        [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
        public DateTime CreationDate { get; set; } = DateTime.UtcNow;

        [DatabaseGenerated(DatabaseGeneratedOption.Computed)]
        public DateTime? ModifyDate { get; set; } = DateTime.UtcNow;

        public DateTime? DeleteDate { get; set; }

        public bool IsDeleted { get; set; } = false;
        public bool CanBeEdited { get; set; } = true;
    }

Kod odpowiedzialny za update:

        public void Update(Meal entityToUpdate)
        {
            dbContext.Update(entityToUpdate);
        }
0

nie za dużo tych Id?

0

@WeiXiao: dlaczego? Ten w tabeli intersection jest po prostu przykryty jako typ int, zamiast guid

0

W klasie bazowej masz właściwość o nazwie Id, w klasie pochodnej MealProduct redefiniujesz tę właściwość z innym typem - tutaj to nawet kompilator/analyzer będzie krzyczał że robisz fikołki.

Ponadto dajesz atrybut świadczacy o tym, że chcesz z int Id w MealProduct w tabeli zrobić klucz główny, po czym w fluent api tworzysz klucz kompozytowy.

Więc tak w zasadzie na cholerę Id w MealProduct?

EF Core to dziwaczne narzędzie z dziwnym zachowaniem w nieoczekiwanych sytuacjach, o którym dowiadujesz się ze StackOverflow, czy issues z githuba, a ty jeszcze na własne życzenie mieszasz fluent api z data annotations...

Ciekawe co za bajzelek wygenerowało w pliku z migracja schematu bazy danych po takim złaczeniu dwoch swiatow.

1 użytkowników online, w tym zalogowanych: 0, gości: 1, botów: 0