EFCore удаляет ссылочные объекты - PullRequest
1 голос
/ 01 мая 2019

У меня есть две сущности в проекте: SupplierFinishingItem и ProductOptionListItem.

ProductOptionListItem ссылается на другое с помощью свойства навигации.

Когда я пытаюсь создать 1 ProductOptionListItem, ссылаясь на SupplierFinishingItem, он работает и сохраняет отношение в базе данных.

Но когда я пытаюсь создать 2 или более ProductOptionListItem, ссылающихся на один и тот же SupplierFinishingItem, только первая сущность имеет отношение, сохраненное в базе данных.Остальные сохраняются со ссылкой null.

Мне удалось воспроизвести его в самом маленьком консольном приложении, которое я мог:

using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Linq;

namespace relationship_test
{
    class Program
    {
        static void Main(string[] args)
        {
            using (var db = new DatabaseContext())
            {
                var finishing = new SupplierFinishingItem { Name = "Finishing"};
                db.Finishings.Add(finishing);

                db.SaveChanges();

                db.Options.Add(new ProductOptionListItem { Name = "Option 1", SupplierFinishingItem = finishing });
                db.Options.Add(new ProductOptionListItem { Name = "Option 2", SupplierFinishingItem = finishing });
                db.Options.Add(new ProductOptionListItem { Name = "Option 3", SupplierFinishingItem = finishing });

                db.SaveChanges();
            }
        }
    }

    public class DatabaseContext : DbContext
    {
        public DbSet<ProductOptionListItem> Options { get; set; }
        public DbSet<SupplierFinishingItem> Finishings { get; set; }

        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            optionsBuilder.UseSqlServer(
                @"Data Source=(localdb)\MSSQLLocalDB;Initial Catalog=entity-test;Integrated Security=True;Connect Timeout=30;Encrypt=False;TrustServerCertificate=False;ApplicationIntent=ReadWrite;MultiSubnetFailover=False");
        }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            modelBuilder.Entity<ProductOptionListItem>()
                .HasOne(p => p.SupplierFinishingItem)
                .WithMany(s => s.UsedBy)
                .HasForeignKey(p => p.SupplierFinishingItemId)
                .OnDelete(DeleteBehavior.Restrict);
        }
    }

    public class ProductOptionListItem
    {
        public Guid Id { get; set; }
        public string Name { get; set; }
        public Guid? SupplierFinishingItemId { get; set; }
        public SupplierFinishingItem SupplierFinishingItem { get; set; }
    }

    public class SupplierFinishingItem
    {
        private HashSet<ProductOptionListItem> _usedBy;

        public Guid Id { get; set; }
        public string Name { get; set; }
        public IEnumerable<ProductOptionListItem> UsedBy => _usedBy?.ToList();
    }
}

В результате после запуска этого кода получится следующая база данных:

enter image description here

Как видите, только Option 1 имеет SupplierFinishingItemId, два других NULL.

Что мне здесь не хватает?

1 Ответ

3 голосов
/ 01 мая 2019

Проблема вызвана реализацией свойства навигации по коллекции:

public IEnumerable<ProductOptionListItem> UsedBy => _usedBy?.ToList();

Возвращение нового экземпляра списка из получателя каким-то образом сбивает с толку код исправления свойства навигации EF Core и производит вышеупомянутый эффект (даже до вызова SaveChanges ()).

Решение состоит в том, чтобы исправить реализацию так, чтобы она не возвращала новый список при каждом вызове (что в любом случае считается плохой практикой для получателя свойства), например:

public IEnumerable<ProductOptionListItem> UsedBy => _usedBy ?? (_usedBy = new HashSet<ProductOptionListItem>());

или настройте EF Core для непосредственного использования вспомогательного поля:

modelBuilder.Entity<ProductOptionListItem>()
    .HasOne(p => p.SupplierFinishingItem)
    .WithMany(s => s.UsedBy)
    .HasForeignKey(p => p.SupplierFinishingItemId)
    .OnDelete(DeleteBehavior.Restrict)
    .Metadata.PrincipalToDependent.SetPropertyAccessMode(PropertyAccessMode.Field); // <--
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...