EF6 AutoMapper6 Родительский/дочерний элемент отличается поведением

Я только что обновил все приложение WCF с EF4/AutoMapper 1.1 на EF6/AutoMapper 6.0.0.2, и поведение изменилось.

У меня это не работает: Entity Framework - Добавить дочернюю сущность

До :

child.Parent = parentObject

OR

parentObject.Children.Add(child)

был тот же результат в реальном времени (во время отладки == перед SaveChanges), поэтому я решил использовать child.Parent = parentObject для удобства чтения. child.Parent = parentObject автоматически добавил дочерний элемент в parentObject. Ребенок также был добавлен в базу данных.

Теперь: child.Parent = parentObject больше недостаточно (дочерний элемент не добавляется в базу данных), я должен добавить parentObject.Children.Add(child). Иногда мне нужна ссылка child.Parent = parentObject, поэтому приходится писать обе строки. Может кто-нибудь объяснить мне, почему это больше не работает?

Также: я мог бы написать before:

Mapper.CreateMap< Patient, PATIENTENTITY >()
                .ForMember(dest => dest.Gender, opt => opt.ResolveUsing< PatientGenderResolver >())
                .ForMember(dest => dest.REF_GENDER, opt => opt.Ignore())

где dest.Gender — это PK (int), а PatientGenderResolver находит идентификатор (int) пола в таблице REF_GENDER. Этого сопоставления было достаточно, чтобы установить PATIENTENTITY.REF_GENDER в реальном времени благодаря преобразователю идентификаторов.

Теперь идентификатор установлен, но PATIENTENTITY.REF_GENDER остается пустым. Также я попытался установить PATIENTENTITY.REF_GENDER напрямую с преобразователем, но он добавил пол в таблицу REF_GENDER...

Итак, еще раз, может кто-нибудь объяснить мне, почему это больше не работает?

ИЗМЕНИТЬ Некоторые уточнения: До:

    patientEntity = Mapper.PatientToEntity(patientModel);
    //patientEntity.REF_GENDER is null
    Context.PATIENTENTITIES.AddObject(patientEntity);
    //patientEntity.REF_GENDER is set !
    Context.SaveChanges();

Сейчас:

patientEntity = Mapper.PatientToEntity(patientModel);
//patientEntity.REF_GENDER is null
Context.PATIENTS.Add(patientEntity);
//patientEntity.REF_GENDER is still null !
//patientEntity.REF_GENDER = Context.REF_GENDER.Find(patientEntity.Gender);//I am obliged to add this line everywhere for every REF !
Context.SaveChanges();

Я предполагаю, что две проблемы, которые у меня есть, связаны

EDIT Я просто возвращаюсь к своему проекту. Теперь у меня есть EF6 и Automapper 1.1. Проблемы точно такие же, поэтому я думаю, что Automapper не участвует.

ИЗМЕНИТЬ Я могу обойти проблему REF_GENDER с помощью

patientEntity = Mapper.PatientToEntity(patientModel, Context);
public PATIENT PatientToEntity(Patient patient, EntityContainer context)
{
    PATIENT entity = AutoMapper.Mapper.Map<Patient, PATIENT>(patient);
    if (patient.Id == null || patient.Id == Guid.Empty)
        entity.PatientId = Guid.NewGuid();
    else
        entity.PatientId = patient.Id;

    entity.REF_GENDER = context.REF_GENDER.Find(entity.Gender);

    return entity;
}

По-видимому, контекст должен быть таким же, иначе в базу данных будет добавлен новый REF_GENDER.


person Flou    schedule 10.10.2018    source источник


Ответы (1)


Вы не упоминаете об этом явно, но вы не только перешли с EF 4 на 6, но и с ObjectContext на DbContext. Это огромная разница в поведении классов сущностей.

В ObjectContext API сгенерированные классы сущностей были заполнены кодом, тесно взаимодействующим с контекстом, в котором они были задействованы. Ссылочное свойство, подобное child.Parent, выглядело бы так:

public Parent Parent
{
    get
    {
        return ((IEntityWithRelationships)this).RelationshipManager.GetRelatedReference<Parent>("model.FK_Child_Parent", "Parent").Value;
    }
    set
    {
        ((IEntityWithRelationships)this).RelationshipManager.GetRelatedReference<Parent>("model.FK_Child_Parent", "Parent").Value = value;
    }
}

Таким образом, установка этого свойства является установкой свойства Value объекта EntityReference<Parent>. Код EF4.1 не является общедоступным, поэтому мы можем только догадываться, что происходит внутри. Ясно одно: он изменяет состояние childs на Added -- если child еще не было присоединено к контексту.

К счастью, EF отказалась от этой жесткой привязки к поставщику, когда выпустила DbContext API (в EF 4.1 не меньше). Теперь это сгенерированное свойство не что иное, как автоматическое свойство:

public Parent Parent { get; set; }

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

Цена (если хотите) заключается в том, что EF не может так тщательно отслеживать все, что происходит, как раньше. Раньше все классы сущностей наследовались от EntityObject, и EF мог отслеживать все их взаимодействия. Заявление...

child.Parent = parentObject;

будет втягивать еще неизвестный child в контекст через прикрепленный parentObject.

Теперь, когда кто-то устанавливает child.Parent, никто, кроме child, не знает, что произошло, даже Parent. Это означает, что EF не может каким-либо образом узнать об этом изменении, когда его средство отслеживания изменений выполнит DetectChanges (что происходит очень часто).

Вот почему, используя DbContext, вы должны сами добавить нового потомка в контекст, либо явно задав его состояние, либо добавив его в context.Children. Или добавив его к parent.Children, что является изменением, которое средство отслеживания изменений может обнаружить, если parent прикреплено.

person Gert Arnold    schedule 13.10.2018
comment
Вы правы, @Gert Arnold, переход от ObjectContext к DbContext - это огромная разница в поведении классов сущностей. Спасибо за ваш ответ и форматирование. Наконец, я добавил child.Parent = parentObject AND parentObject.Children.Add(child), чтобы мой код снова работал. И я установил рефы в маппере, как я написал. - person Flou; 16.10.2018
comment
Я только что закончил миграцию через неделю. Я тестирую прирост производительности, и во всех случаях он хуже, чем ef4, в отличие от docs.microsoft.com/fr-fr/ef/ef6/fundamentals/performance/ сказал... Я очень разочарован :( - person Flou; 17.10.2018
comment
Да, это неожиданно. Вы можете попробовать задать вопрос о конкретном случае. Возможно, в свете вышеизложенного есть некоторые вещи, которые вы должны делать по-другому в EF6 по сравнению с EF4, например, используя внешние ассоциации ключей вместо независимых ассоциаций. - person Gert Arnold; 17.10.2018
comment
1 - Я думаю, что уже использую FK вместо независимой ассоциации, но я не уверен. Я имею в виду, что у меня уже есть один FK public int PrincipalEntity_Id { get; задавать; } (без [ForeignKey(PrincipalEntity_Id)]) И одного свойства навигации public Principal PrincipalEntity { get; задавать; } но я не использую [ForeignKey(PrincipalEntity_Id)], потому что это автоматически сгенерированный код, и я подумал, что он достаточно хорош для понимания EF6 - person Flou; 19.10.2018
comment
2 - Я изучаю возможности повышения производительности загрузки. Я только что заметил с помощью SQL Profiler, что запрос where использует ленивую загрузку: только один выбор/где для самого высокого объекта. Затем я заметил, что AutoMapper делает много select/where при отображении из-за ленивой загрузки. Это не кажется очень эффективным! Поэтому я попробовал методы .Include (многие) в основном запросе: это запрос с несколькими соединениями, который кажется мне более эффективным. Но тогда сопоставление по-прежнему выполняет то же самое количество select/where, поэтому мне интересно, оправдан ли AutoMapper? - person Flou; 19.10.2018
comment
Я предлагаю вам задать новый вопрос с достаточным количеством деталей, чтобы ответить на него. Я не могу ответить на это из информации в ваших комментариях, и это также выходит за рамки этого вопроса. - person Gert Arnold; 19.10.2018
comment
3 - Еще забыл... производительность не самая плохая, я просто забыл про холодное и теплое исполнение, в итоге результаты одинаковые - person Flou; 19.10.2018