Коллекция собственных типов никогда не обновляется в EF Core - PullRequest
1 голос
/ 19 февраля 2020

У меня есть агрегат, определенный следующим образом:

public class Product {
    public int LocalId { get; private set; }
    public Something Something { get; private set; }
    public ICollection<Price> Prices { get; private set; }
}

public class Something {
    public string Name { get; set; }
}

public class Price {
    public int Type { get; set; }
    public decimal Value { get; set; }
}

И схема, определенная следующим образом:

private void DefineProduct(ModelBuilder builder) =>
    builder
        .Entity<Product>(builder =>
        {
            builder.HasKey(p => p.LocalId);
            builder
                .OwnsOne(p => p.Something, smth =>
                {
                    smth.ToTable("somethings");
                })
                .OwnsMany(p => p.Prices, pp =>
                {
                    pp.ToTable("prices");
                });
        });

Когда запрашивается изменение цены, я делаю это (внутри продукта метод для краткости не включен):

Prices.First(p => p.Type == type).Value = newValue;

И затем я пытаюсь сохранить продукт следующим образом:

public async Task UpdateProperties(Product product, IEnumerable<object> props)
{
    _context.Attach(product);
    _context.Update(product);

    foreach (var prop in props)
    {
        _context.Update(prop);
    }

    try
    {
        await _context.SaveChangesAsync();
    } 
    catch (Exception ex)
    {
        Console.WriteLine("Who the hell allowed such a bug to go into a production release?");
    }
}

Теперь я должен упомянуть, что продукт поступает из первоначального запроса чьи результаты не отслеживались (через вызов AsNoTracking()), поэтому я вызываю метод Attach в первой строке тела метода. Проблема в том, что я нажимаю на этот оператор catch с сообщением об исключении, которое гласит:

Предполагается, что операция с базой данных затронет 1 строку (и), но фактически затронет 0 строк. Данные могут быть изменены или удалены с момента загрузки объектов. См. http://go.microsoft.com/fwlink/?LinkId=527962 для получения информации о понимании и обработке оптимистических c исключений параллелизма. "}

Дело в том, что я не обновляю тот же продукт в другом месте, и это единственное место, где его касаются. Также я использую AsNoTracking в качестве значения по умолчанию. Если я закомментирую строку с _context.Update(prop);, то исключение не возникнет, но цена не обновляется. Кроме того, если я не обновлю это Сбор цен, но собственность Something, все идет хорошо. Что. Черт.

Ответы [ 2 ]

0 голосов
/ 19 февраля 2020

EF Базовая документация для Коллекции собственных типов прямо заявляют, что вы должны определить принадлежащий объект PK (в отличие от OwnsOne, где теневой FK обычно используется в качестве PK).

Следовательно, вам нужно либо определить свой собственный PK (например, Id, который вы сделали), либо составной PK - например, если Price.Type уникален внутри владельца, тогда вы можете использовать что-то вроде

pp.HasKey("LocalId", "Type");

и избегайте дополнительного столбца Id.

0 голосов
/ 19 февраля 2020

Я публикую этот ответ для будущих потерянных путешественников, хотя я бы с радостью услышал объяснение от кого-то, кто знает больше меня о EF Core. Я также думаю, что понимаю причину такого поведения, но я не уверен. Мой код начал работать правильно, когда я добавил поле Id для объекта Price. Я подозреваю, что без явно видимого свойства id никакое количество вложений или обновлений не заставит EF увидеть и этот объект. Я хотел бы приветствовать раздел документации об этом ...

...