Как реализовать модель с использованием шаблона нулевого объекта - PullRequest
0 голосов
/ 26 февраля 2020

У меня есть объект значения и сущность, которая использует указанный объект значения. Я использую шаблон нулевого объекта для указанного объекта значения. Мой объект значения выглядит примерно так:

public class Reference {
  public static Reference Empty;
  private string _value;

  static Reference() {
    Empty = new Reference(String.Empty);
  }
  public Reference(string value) {
    _value = value;
  }
  private Reference() {
    _value = default!;
  }
  public string Value => _value;
}

А моя сущность:

public class Payment {
  private Guid _id;
  private decimal _amount;
  private Reference _reference;

  public Payment(decimal amount) {
    _id = Guid.Empty; //ef will generate the id
    _reference = Reference.Empty;
    _amount = amount;
  }
  private Payment() {
    _id = default;
    _reference = default!;
    _amount = default;
  }

  public Guid Id => _id;
  public decimal Amount => _amount;
  public Reference Reference => _reference;
}

Теперь класс Reference настроен в EF-Core с использованием отношения OwnsOne. Примерно так:

internal class PaymentConfiguration
  : IEntityTypeConfiguration<Payment> {

  public void Configure(EntityTypeBuilder<Payment> builder) {
    builder.ToTable("Payments");
    builder.HasKey(x => x.Id);
    builder.OwnsOne(x => x.Reference, y => {
      y.Property(z => z.Value)
        .HasColumnName("Reference")
        .IsRequired();
    });
  }
}

Добавление платежа выглядит следующим образом:

var payment = new Payment(5000m);
_context.Payments.Add(payment);
await _context.SaveChangesAsync();

Это работает довольно хорошо, но в тот момент, когда я пытаюсь добавить два платежа:

_context.Payments.Add(new Payment(5000m));
_context.Payments.Add(new Payment(6000m));
await _context.SaveChangesAsync();

Я получаю сообщение об ошибке: The property 'PaymentId' on entity type 'Reference' is part of a key and so cannot be modified or marked as modified. To change the principal of an existing entity with an identifying foreign key first delete the dependent and invoke 'SaveChanges' then associate the dependent with the new principal.

После такой большой отладки я обнаружил, что проблема в том, что оба объекта Payment используют один и тот же ссылочный объект, определенный Reference.Empty , Действительно, если я изменю свой код на следующий, проблема прекратится:

  public Payment(decimal amount) {
    _id = Guid.Empty; //ef will generate the id
    _reference = new Reference(String.Empty); //instead of Reference.Empty;
    _amount = amount;
  }

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

Теперь мой вопрос. Есть ли способ настроить Ef-Core, чтобы остановить это поведение? Или есть обходной путь для использования шаблона нулевого объекта? Я использую C # 8, NET Core 3 и Entity Framework Core 3. Заранее спасибо.

1 Ответ

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

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

https://github.com/dotnet/efcore/issues/4073

Если вы просто хотите уникальный индекс в свойстве, затем используйте этот код:

modelBuilder.Entity (). HasIndex (u => u.Name) .IsUnique ();

Редактировать:

Когда настраивая ownsone, вы создаете неявный ключ для Reference на основе Payment (в этом случае ключ PaymentId). Вот почему компилятор интерпретирует его как таковой и выдает исключение «Свойство PaymentId» для типа сущности «Ссылка» [...] "

Посмотрите, что делает Microsoft c по этому вопросу:

Типы владельцев, настроенные с помощью OwnsOne или обнаруженные с помощью ссылочной навигации, всегда имеют однозначное отношение с владельцем, поэтому им не нужны свои собственные значения ключей, поскольку значения внешнего ключа уникальны.

https://docs.microsoft.com/en-us/ef/core/modeling/owned-entities#implicit -keys

Наконец, я предлагаю вам наследовать класс ValueObject вместо его создания. Кроме того, создайте свой объектный класс-значение как абстрактный класс и исключите OwnsOne в следующем коде:

builder.OwnsOne(x => x.Reference, y => {
      y.Property(z => z.Value)
        .HasColumnName("Reference")
        .IsRequired();
    });
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...