2017-01-11 5 views
2

На моей странице редактирования я получаю эту ошибку:AutoMapper дает недопустимое исключение операции

Attaching an entity of type failed because another entity of the same type already has the same primary key value.

Итак, я исследовал, что и пришел к this.

Это привело меня к использованию Auto-Mapper, чтобы упростить ситуацию.

Вот мой код:

if (db.TableName.Find(value.ID).stringProperty.Equals(value.stringProperty, StringComparison.CurrentCultureIgnoreCase)) 
{ 
    Table inContextVariable = db.TableName.Find(value.ID); 

    Mapper.Initialize(config => config.CreateMap<ModelName, ModelName>()); 

    Mapper.Map<ModelName, ModelName>(value, inContextVariable); 

    db.Entry(inContextVariable).State = EntityState.Modified; 
    db.SaveChanges(); 
    return RedirectToAction("Index"); 
} 

Это приводит меня к этой ошибке:

Additional information: The operation failed: The relationship could not be changed because one or more of the foreign-key properties is non-nullable. When a change is made to a relationship, the related foreign-key property is set to a null value. If the foreign-key does not support null values, a new relationship must be defined, the foreign-key property must be assigned another non-null value, or the unrelated object must be deleted.

Эта таблица имеет 1 внешний ключ, и когда я отлаживать, что внешний ключ имеет значение 11 , поэтому мне трудно понять эту ошибку. Кроме того, это мой первый раз, используя Auto-Mapper, так что голый со мной.

Любая помощь очень ценится.


UPDATE


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

| ID | First Name | Last Name |   Email   | 
---------------------------------------------------------------------------- 
    1   John    Doe    [email protected] 
    2   Christopher  Columbus  | [email protected] | 

И так далее и так далее .. Теперь , поскольку у человека может быть только 1 адрес электронной почты, у вас должны быть гарантии против других пользователей, когда они создают/редактируют записи.

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

Этот адрес электронной почты уже существует! Пожалуйста, введите другой адрес электронной почты

Вот этот код:

if (db.TableName.Any(x => x.Email.Equals(value.Email, StringComparison.CurrentCultureIgnoreCase))) 
{ 
    ModelState.AddModelError("Email", "This email already exists!"); 
    return View(value); 
} 

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

НО, когда этот пользователь создал свой счет, их адреса электронной почты теперь в таблице базы данных .. поэтому, когда они идут на страницу редактирования, с оригинальной электронной почте, и они попали «Сохранить», код проверит это письмо на все адреса электронной почты в базе данных, и он увидит, что он уже находится в базе данных, в результате чего появляется сообщение об ошибке (пожалуйста, измените адрес электронной почты .etc) ..

Так что я нуждается в попытке создать эффективный способ проверки, чтобы увидеть когда пользователь находится на редактирования страницы и хитов «Сохранить» с оригинальной электронной почте, что он не говорит им изменить их электронную почту.

Так что у меня есть этот код:

if (db.TableName.Find(value.ID).Email.Equals(value.Email, StringComparison.CurrentCultureIgnoreCase)) 
{ 
    var inContextVariable = db.TableName.Find(value.ID); 
    MappingMethods.MapModelName(inContextVariable , value); 

    \\ without the mapping.. I receive the error saying 'Attaching an entity of type failed because another entity of the same type already has the same primary key value.' 

    db.Entry(inContextVariable).State = EntityState.Modified; 
    db.SaveChanges(); 
    return RedirectToAction("Index"); 
} 
+0

Я бы рекомендовал инициализировать ваши сопоставления при развороте, а не при использовании. Обычно мы запускаем метод AutoMapperConfig.Configure() в Startup.cs, и этот метод инициализирует сопоставления. – nurdyguy

+0

Я думаю, что ваш основной вопрос здесь больше связан с EF, чем с чем-либо. Попробуйте создать модель представления, чтобы разместить копию и карту из переменной inContextVariable в vew. Затем вы можете использовать эту переменную в другом месте, не беспокоясь о EF. (Предполагая, что это цель.) – nurdyguy

+0

@nurdyguy вы можете предоставить код, чтобы я мог лучше понять? –

ответ

1

Вот основная логика я хотел бы использовать здесь

Table inContextVariable = db.TableName.Find(value.ID); 
if(!inContextVariable.Email.Equals(value.Email, StringComparison.CurrentCultureIgnoreCase)) 
{ 
    // user input a new email so make sure it doesn't match anyone else 
    if(db.TableName.Any(x => x.Email.Equals(value.Email, StringComparison.CurrentCultureIgnoreCase) && x.ID != value.ID)) 
    { 
     // user's new email DOES match someone else's email so handle it 
    } 
    else 
    { 
     // no match so do the update 

    } 

} 

Нет необходимости обрабатывать внешнюю еще потому, что они не изменили свою электронную почту, так что вы ничего не делаете!

Примечание: здесь у вас будет состояние гонки! Если два человека одновременно отправляются в одно и то же время, то проверка может пройти, но оба они получают тот же адрес электронной почты. Например, скажем, они оба переходят на [email protected], но проверка второго человека выполняется до сохранения первого лица. Но такая жизнь для веб-приложений.

+0

Любой документированный способ справиться с состоянием гонки? Хотя это очень маловероятно для этого конкретного приложения? –

+0

Ну, вероятность - относительно популяции пользователей приложения. В этом было бы просто большое приложение. Я думаю, что лучше всего поставить уникальное ограничение на столбец db и обернуть его в try-catch. Но я уверен, что другие не согласятся с этим. Чувак, условия гонки сосут ... – nurdyguy

+0

Хмм, держу пари, они это делают. Думаю, когда я буду руководить созданием такого приложения, я буду больше смотреть на него. –

0

С помощью @nurdyguy я пришел с этой идеей. Я создал еще один класс с именем MappingMethods который содержит этот метод:

public static void MapModelName(ModelName inContext, ModelName outOfContext) 
{ 
    inContext.ID = outOfContext.ID; 
    inContext.stringProperty= outOfContext.stringProperty; 
    inContext.OwnerID = outOfContext.OwnerID; \\ foreign key 
    inContext.DateCreated = outOfContext.DateCreated; 
} 

Тогда в моем Edit Action:

if (db.TableName.Find(value.ID).stringProperty.Equals(value.stringProperty, StringComparison.CurrentCultureIgnoreCase)) 
{ 
    var inContextVariable = db.TableName.Find(value.ID); 
    MappingMethods.MapModelName(inContextVariable , value); 

    db.Entry(inContextVariable).State = EntityState.Modified; 
    db.SaveChanges(); 
    return RedirectToAction("Index"); 
} 

Это работало.

+0

Нет, это полностью не правильно –

+0

@AlexOvechkin описание? –

+0

@BviLLe_Kid вы пытаетесь сохранить новую строку в db или обновить существующую строку? – nurdyguy

0

Вся идея automapper заключается в том, чтобы избежать таких методов в вашем статическом классе. Представьте себе, если у вас было 100 свойств? (например, например). Will u copy-paste obj1.prop1 = obj2.prop1?

Прежде всего я хотел бы предложить создать модель - smth. которые будут представлять собой представление вашей модели. Да, вы можете создать копию объекта с автоматом, но он не был предназначен для этого.

Попробуйте сделать что-л так:

  1. создать модель представления - она ​​может быть такой же, как модель, или нет - все это зависит от вас (ВТН это не очень хорошая практика, чтобы передать ваши DB модель в представление)
  2. карты Ваша модель для просмотра-модели и virce-verce, как это:

    Mapper.CreateMap() .ForMember (d => d.DateCreate, d => d.MapFrom (x => x.DateCreated));

+0

Я 100% согласен с вашим объяснением AutoMapper и его целью. AutoMapper фантастичен для такого рода вещей. Это просто совершенно необязательно для фактической проблемы OP. – nurdyguy

+0

@nurdyguy Я не думаю, что мы на одной странице ..... позвольте мне обновить свой вопрос с помощью оператора 'if', который я пытаюсь объяснить. –

+0

@nurdyguy см. Мое обновление для лучшего уточнения –