вложенный тип собственности не сохраняется при обновлении в базе данных - PullRequest
0 голосов
/ 25 февраля 2019

У меня есть следующие классы:

Заказ:

public class Order {
  private Order()
  {
      //some code to initialise object
  }
  //more properties
  public Prepayment Prepayment { get; private set; }
  //more methods and properties
}

Предоплата:

public class Prepayment:ValueObject<Prepayment>
{
    private Prepayment()
    {   
    }

    public Money AmountPrepaid { get; private set; }
    public bool HasPrepaymentBeenTaken => AmountPrepaid.Amount > 0;
}

Деньги:

public class Money {
        private Money()
        {
        }
        private Money(decimal amount)
        : this()
        {
            Amount = amount;
        }
        public decimal Amount { get; private set; }
}

Тогда яКласс заказа отображается в базе данных следующим образом:

modelBuilder.Entity<Order>()
                .OwnsOne(x => x.Prepayment,
                    prepayment =>
                    {
                        prepayment.OwnsOne(x => x.AmountPrepaid,
                            amountPrepaid =>
                            {
                                amountPrepaid.Property(x => x.Amount)
                                    .HasColumnName("PrepaymentAmount")
                                    .HasColumnType("decimal(7,2)");
                            });
                    });

Код репозитория для SaveChanges:

public async Task<int> SaveAsync(Order order)
{
    if (order.Id == 0)
    {
        await _context.AddAsync(order);
    }
    else
    {
        _context.Update(order);
    }

    return await _context.SaveChangesAsync();
}

Пожалуйста, поймите, я удалил все не важные свойства из кода, чтобы сделатьпример более понятен.

Приведенный выше код хорошо работает для сценария INSERT, где Предоплата -> Деньги -> Сумма правильно сохраняется в базе данных.UPDATE хотя, кажется, не отражается в базе данных.

Обратите внимание, у меня есть несколько Owned Types в этой модели, и все они работают хорошо.Насколько я могу сказать, единственное отличие состоит в том, что свойство Prepayment имеет другой вложенный тип Owned - Money.

Order объект класса, переданный в хранилище, сначала извлекается из базы данных, затемизменения применяются к этому экземпляру и, наконец, сохраняются обратно в базу данных.Другие свойства, такие как Customer, не упомянутые в примере, также OwnedType и UPDATE работают как положено.

На всякий случай - код, используемый для получения объекта до обновления:

public async Task<Order> GetOrderByIdAsync(int orderId)
{
    var result = (from order in _context.Orders
                  where order.Id == orderId
                  select order).Include(x => x.OrderLines);

    return await result.FirstOrDefaultAsync();
}

Точная версия Entity Framework Core, которую я использую: 2.2.0

Любая помощь будет принята с благодарностью.Спасибо.

РЕДАКТИРОВАТЬ:

Код, который обновляет данные, выглядит следующим образом:

public async Task<int> Handle(EditOrderCommand request, CancellationToken cancellationToken)
{
    var order = await _orderRepository.GetOrderByIdAsync(request.Id);

    var customer = new Customer(
        request.FirstName,
        request.LastName,
        request.TelephoneNumber);

    var prepayment = new Prepayment(
        Money.SomeMoney(
            request.PrepaymentAmount
            )
        );

    order.ApplyChanges(
            request.UserId, 
            request.AdditionalInformation,
            collectionDate,
            customer,
            prepayment);

    await _orderRepository.SaveAsync(order);

    return order.Id;
}

И часть метода ApplyChanges, который устанавливает предоплату:

private void SetPrepayment(Prepayment prepayment)
{
    Prepayment = prepayment ?? throw new ArgumentNullException(nameof(prepayment));
}

1 Ответ

0 голосов
/ 26 февраля 2019

Проблема имеет что-то общее с вложенным типом сущности.

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

Принадлежащие типы сущностей не могут использоваться для реализации объектов-значений, потому что даже они «принадлежат», согласно терминологии EF Core, они все еще являются «сущностями» (сскрытая теневая PK), поэтому они отслеживаются по ссылке и следуют тем же правилам, что и другие ссылки на сущности - самое главное, для каждого определяющего PK навигации должен быть только один экземпляр объекта.

Короче говоря, они должны быть выделены один раз, а затем мутированы через свои примитивные свойства.В то время как вы делаете, мутируете ссылки с неизменными объектами.

Трудно дать вам хороший совет из-за вышеупомянутых нарушений правил EF Core.Единственный обходной путь - убедиться, что все исходные ссылки на объекты не отслеживаются контекстом.

Например, если реализация GetOrderByIdAsync использует запрос AsNoTracking, следовательно, ни order, ни order.Prepayment и order.Prepayment.AmountPrepaid экземпляры не отслеживаются _context, тогда _context.Update(order) будет работать.

Это также будет работать, если вы вручную отсоедините их перед вызовом ApplyChanges (требуется доступ к контексту db):

_context.Entry(order).State = EntityState.Detached;
_context.Entry(order.Prepayment).State = EntityState.Detached;
_context.Entry(order.Prepayment.AmountPrepaid).State = EntityState.Detached;

order.ApplyChanges(...);

await _orderRepository.SaveAsync(order); // _context.Update(order);

Похоже, AsNoTracking - лучший вариант.Вы можете установить его по умолчанию для всех запросов, установив ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking; в своем конструкторе контекста БД и использовать AsTracking() там, где это необходимо.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...