Свойство собственного типа не сохраняется для измененного объекта в EF Core

Я пытаюсь добиться чего-то в EF Core, что очень хорошо сработало для меня в EF 6.

Я сериализую содержимое свойства List<T> в виде строки Json в БД. <T> может быть чем угодно, поскольку Json.Net заботится о сериализации всего, что мы ему подбрасываем. Моя коллекция предоставляет строковое свойство Json и сама заботится о сериализации/десериализации.

Этот подход удобен и эффективен для структурированных вложенных данных, где реляционная модель привела бы к ненужным накладным расходам и сложности.

В EF 6 я бы сделал что-то вроде этого:

[ComplexType]
public class SelfSerializingCollection<T> : Collection<T>
{
    public void AddRange(IEnumerable<T> collection)
    {
        foreach (var item in collection)
        {
            Add(item);
        }
    }

    protected string Json
    {
        get { return JsonConvert.SerializeObject(this); }
        private set
        {
            Clear();

            if (value == null)
            {
                return;
            }

            AddRange(JsonConvert.DeserializeObject<T[]>(value));
        }
    }
}

EF 6 не поддерживает сопоставление универсальных типов, поэтому мы предоставим конкретную реализацию для каждого типа списка, который нам может понадобиться:

public class PhoneNumberCollection : SelfSerializingCollection<PhoneNumber> { }

А затем используйте его следующим образом:

public class Contact {
    public PhoneNumberCollection PhoneNumbers { get; set; }
}

И это было все. Теперь я пытаюсь добиться того же в EF Core 2.0.

Вот что у меня есть до сих пор. Класс SelfSerializingCollection<T> не изменился.

public class Contact {
    // EF Core supports generic types so we don't need a concrete implementation
    // for each collection type.
    public SelfSerializingCollection<PhoneNumber> PhoneNumbers { get; set; }
}

public class MyDbContext : DbContext
{
    // ...

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        // EF Core does not support the [ComplexType] attribute
        // but we now have .OwnsOne() in EF Core 2
        modelBuilder.Entity<Contact>().OwnsOne(p => p.PhoneNumbers);
    }
}

Все идет нормально. EF Core сопоставляет столбец PhoneNumbers_Json в базе данных. Чтение и создание новых записей работает просто отлично.

Но, в отличие от EF 6, любые изменения в коллекции PhoneNumbers существующего объекта не сохраняются обратно в базу данных. Я обновляю типичным методом PUT с помощью _context.Entry(contactModelFromRequestBody).State = EntityState.Modified; - так же, как и раньше. Но по какой-то причине EF Core не сохраняет мое строковое свойство Json обратно в БД для объекта Modified.

Любые идеи?


person Matt Jenkins    schedule 22.10.2017    source источник


Ответы (2)


EF Core рассматривает принадлежащие типы как типы сущностей без собственного идентификатора, поэтому свойства, указывающие на принадлежащий тип, обрабатываются как свойства навигации. И установка состояния родительского объекта на Modified не распространяется каскадом на свойства навигации.

Однако метод Update для DbContext или DbSet каскадирует, поэтому вместо

_context.Entry(contactModelFromRequestBody).State = EntityState.Modified;

вы должны использовать

_context.Update(contactModelFromRequestBody);
person Ivan Stoev    schedule 22.10.2017
comment
Намного приятнее :) Спасибо. - person Matt Jenkins; 22.10.2017

Кажется необходимым вручную установить измененное состояние объекта для моих собственных типов:

_context.Entry(contact).Reference(p => p.PhoneNumbers)
    .TargetEntry.State = EntityState.Modified;

Это работает, хотя я все еще надеюсь найти более общее решение, которое не требует от меня отмечать все принадлежащие свойства таким образом при каждом обновлении.

person Matt Jenkins    schedule 22.10.2017