Я полагаю, что проблема связана с передачей сущностей, которые были загружены другим DbContext, и последующим добавлением ссылок на эти сущности в новом DbContext.
Этот код не имеет особого смысла:
var res = db.Respondents
.Include(user => user.Requester)
.Include(user => user.Provider)
.Where(o => o.Requester.UserId == requester.UserId ).ToList();
Где вы не используете "res" где-либо в коде. Вы используете «а», что, как я полагаю, означает одно и то же?
Во-первых, если вы просто хотите проверить наличие строки, используйте Any()
if (!db.Respondents.Any(x => x.Requester.UserId == requester.UserId))
Вместо загрузки всех доступных объектов, просто чтобы проверить, существуют ли некоторые из них.
Проблемная область, которую я вижу, такова:
tempProvider = TempProvidersList[i];
db.Respondents.Add(new Respondent() { Requester = requester, Provider = tempProvider, Role = TempProvidersList[i].Role });
Это установка ссылки вашего провайдера в новом Ответчике на Пользователя (провайдера), который, вероятно, был загружен из другого экземпляра DbContext.
Для решения этой проблемы любые ссылки, которые вы устанавливаете, должны быть загружены из текущего DbContext:
var providerIds = respondent.Providers.Select(x => x.UPI).ToList();
var providers = db.Users.Where(x => providerIds.Contains(x.UPI)).ToList();
var requester = db.Users.Single(s => s.UserId == respondent.Requester.UserId);
requester.IsPublic = false;
foreach(var providerId in providerIds)
{
if (!db.Respondents.Any(x => x.Requester.UserId == requester.UserId))
{
var provider = providers.Single(x => x.UPI == providerId);
provider.IsPublic = true;
db.Respondents.Add(new Respondent() { Requester = requester, Provider = provider, Role = provider.Role });
}
}
db.SaveChanges();
Я решил загружать всех подходящих провайдеров одним попаданием на основе идентификаторов, а не по одному внутри цикла. В цикле я просто пытаюсь получить тот, который соответствует идентификатору из загруженного набора.
Я бы обернул это блоком try / catch и обработал исключения. Возможные исключения, которые приходят на ум, - это когда пользователь не найден для ProviderId. (Могут ли пользователи быть удалены?) Я сделал foreach по идентификаторам, а не по загруженным ссылкам, потому что в этой коллекции может не быть сущности для каждого идентификатора на основании вышеуказанного условия. (если только 5 из 6 идентификаторов загружают пользователя провайдера, в коллекции идентификаторов будет 6 элементов, а в коллекции сущностей будет только 5)
Еще один момент здесь - использовать Single
вместо FirstOrDefault
, где вы ожидаете одну запись. Это обеспечивает соблюдение этого правила и генерирует исключение, если существует более одной или нет совпадающих записей. Варианты «OrDefault» следует использовать только в том случае, если поиск строки не является ожидаемым результатом. FirstOrDefault
скроет проблемы, если в БД попадет более одного совпадения, и без предложения «OrderBy» вы не сможете рассчитывать, какая ссылка будет возвращена.