Несколько добавленных объектов могут иметь один и тот же первичный ключ
Вот моя модель из трех сущностей: Route, Location и LocationInRoute.
![model]()
следующий метод выходит из строя и получает исключение при его фиксации:
public static Route InsertRouteIfNotExists(Guid companyId, IListLocation> locations)
{
//Loop on locations and insert it without commit
InsertLocations(companyId, routesOrLocations);
RouteRepository routeRep = new RouteRepository();
Route route = routeRep.FindRoute(companyId, locations);
if (route == null)
{
route = new Route()
{
CompanyId = companyId,
IsDeleted = false
};
routeRep.Insert(route);
LocationInRouteRepository locInRouteRep = new LocationInRouteRepository();
for (int i = 0; i < locations.Count; i++)
{
locInRouteRep.Insert(new LocationInRoute()
{
//Id = i,
LocationId = locations[i].Id,
Order = i,
RouteId = route.Id
});
}
}
return route;
}
При выполнении:
InsertRouteIfNotExists(companyId, locations);
UnitOfWork.Commit();
Я получил:
Невозможно определить основной конец отношения "SimTaskModel.FK_T_STF_SUB_LOCATION_IN_ROUTE_T_STF_LOCATION_location_id". Несколько добавленных объектов могут иметь один и тот же первичный ключ.
При расщеплении фиксации и вставки в методе - это работает:
public static Route InsertRouteIfNotExists(Guid companyId, IListLocation> locations)
{
//Loop on locations and insert it without commit
InsertLocations(companyId, routesOrLocations);
UnitOfWork.Commit();
RouteRepository routeRep = new RouteRepository();
Route route = routeRep.FindRoute(companyId, locations);
if (route == null)
{
route = new Route()
{
CompanyId = companyId,
IsDeleted = false
};
routeRep.Insert(route);
LocationInRouteRepository locInRouteRep = new LocationInRouteRepository();
for (int i = 0; i < locations.Count; i++)
{
locInRouteRep.Insert(new LocationInRoute()
{
//Id = i,
LocationId = locations[i].Id,
Order = i,
RouteId = route.Id
});
}
UnitOfWork.Commit();
}
return route;
}
Я хотел бы называть commit один раз и вне метода. Почему в первом примере это не удается и что означает это исключение?
Ответы
Ответ 1
Ошибка вызвана идентификатором внешнего ключа (в отличие от ссылки), который не может быть разрешен. В вашем случае у вас есть LocationInRole, который ссылается на местоположение с идентификатором 0. Существует несколько местоположений с этим идентификатором.
Локациям еще не присвоен идентификатор, поскольку они еще не были сохранены в базе данных, которая возникает при генерации идентификатора. Во втором примере местоположения сохраняются до доступа к их идентификаторам, поэтому это работает.
Вы не сможете полагаться на идентификаторы местоположения, чтобы определять отношения, если вы хотите Сохранить изменения только позже.
Поменяйте следующую строку...
LocationId = locations[i].Id
... для этого...
Location = locations[i]
Затем отношения будут основаны на ссылках объектов, которые не зависят от идентификаторов LocationID.
Ответ 2
В случае, если это будет полезно для будущих читателей, в моем случае эта ошибка возникла из-за неправильно настроенного внешнего ключа в моей базе данных (и модели, сгенерированной из БД).
У меня были таблицы:
Parent (1-1) Child (1-many) Grandchild
а таблица Grandchild непреднамеренно получила иностранный ключ до него родителя (ребенка) и его дедушки (Parent). При сохранении нескольких родительских объектов из нового я получил эту ошибку. Исправлено исправление внешнего ключа.
Ответ 3
Запуская ту же ошибку, я очень подозреваю, что актуальной проблемой было определение местоположения. Проще говоря, в EF Code First я уверен, что это выглядело так:
public class Location
{
public int Id { get; set; }
...
public Location ParentLocation { get; set; }
[ForeignKey("ParentLocation")]
public int ParentLocationId { get; set; }
}
Другими словами, в вопросе ParentLocation/ParentLocationId являются рекурсивной ссылкой на эту таблицу.
ParentLocationId не является Nullable. Это означает, что он будет вставлен с 0, а EF будет жаловаться на Insert, а не на Migrate - даже если правда заключается в том, что когда-то Migration работает, у вас есть таблица EF, которая никогда не позволит вам вставлять.
Единственный способ сделать рекурсивную ссылку обратно на ту же работу таблицы - сделать рекурсивную ссылку нулевой:
public class Location
{
public int Id { get; set; }
...
public Location ParentLocation { get; set; }
[ForeignKey("ParentLocation")]
public int? ParentLocationId { get; set; }
}
Обратите внимание на ?
после int
.
Ответ 4
Для тех, кто ищет это исключение:
В моем случае он не смог установить требуемое свойство навигации.
public class Question
{
//...
public int QuestionGridItemID { get; set; }
public virtual QuestionGridItem GridItem { get; set; }
//...
public int? OtherQuestionID { get; set; }
public Question OtherQuestion { get; set; }
}
//...
question.OtherQuestion = otherQuestion;
questionGridItem.Questions.Add(question);
dataContext.SaveChanges(); //fails because otherQuestion wasn't added to
//any grid item Question collection
Ответ 5
У меня была такая же проблема. с приведенным ниже сценарием.
я думаю, вы должны изменить свой код, например, ниже:
var insertedRoute =routeRep.Insert(route);
.....
insertedRoute.LocationInRoute = new List<LocationInRoute>();
for(....){
var lInRoute = new LocationInRoute(){
....
Route=insertedRoute;
}
insertedRoute.LocationInRoute.Add(lInRoute );
}