У нескольких добавленных объектов может быть один и тот же первичный ключ

81

Вот моя модель из трех объектов: Route, Location и LocationInRoute.
модель

следующий метод не работает и получает исключение при его фиксации:

 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;
            }

Я хотел бы вызвать фиксацию один раз и вне метода. Почему это не удается в первом примере и что означает это исключение?

Наор
источник
9
@ Ладислав Мрнка: У меня нет начальника, и это мой проект. Я действительно не знаю, откуда у вас такое впечатление, что я сразу спрашиваю о ТАК. Вы не единственный, кто пользуется компьютером весь день. Бесплатная консультация? Кто-то дает кому-нибудь гарантии своих ответов? Я считаю, что это форум, где можно задать вопросы, и я этим занимаюсь. У меня много вопросов, и я считаю, что учусь на большом расстоянии благодаря этому форуму и таким людям, как вы. Участие - выбор.
Наор
1
@Ladislav: Я вижу только достаточно хорошо заданный вопрос, и в профиле OP тоже ничего не указано.
Хенк Холтерман
Используете ли вы один и тот же ObjectContext во всем объеме операции или каждый новый репозиторий будет иметь свой собственный ObjectContext?
Акаш Кава
@Akash Kava: Я использую тот же ObjectContext.
Наор

Ответы:

144

Ошибка вызвана идентификатором внешнего ключа (в отличие от ссылки), который не может быть разрешен. В вашем случае у вас есть LocationInRole, который ссылается на Location с идентификатором 0. Существует несколько местоположений с этим идентификатором.

Местоположениям еще не был присвоен идентификатор, потому что они еще не были сохранены в базе данных, когда создается идентификатор. Во втором примере местоположения сохраняются до доступа к их идентификаторам, поэтому это работает.

Вы не сможете полагаться на идентификаторы местоположения для определения отношений, если хотите сохранить изменения только позже.

Поменяйте местами следующую строку ...

LocationId = locations[i].Id

...за это...

Location = locations[i]

Тогда отношения будут основаны на ссылках на объекты, которые не зависят от LocationID.

Скотт Манро
источник
Кто-нибудь из вас может взглянуть на мой пост и рассказать, как я могу это исправить, у меня такая же проблема: stackoverflow.com/questions/26783934/… Я ценю это!
duxfox
Я получаю эту ошибку при развертывании для тестирования env ... нет в dev environemnet каких-либо идей?
Таран,
@Taran Если код идентичен и вы используете один и тот же процесс для тестирования в обеих средах (я бы проверил эти моменты), то это действительно кажется странным. Может быть, вы добавляете только одно местоположение (в соответствии с примером здесь) в dev? Попробуйте добавить хотя бы два.
Скотт Манро
Моя проблема может быть вариацией этой. Я добавлял дочерние объекты в родительскую коллекцию без явной установки родительского свойства для дочерних элементов. Я ожидал, что EF разрешит это, но получал ту же ошибку, что и OP, пока я явно не установил родительское свойство для дочернего элемента. Надеюсь, это кому-то поможет.
Eric H
4

Если это будет полезно для будущих читателей, в моем случае эта ошибка возникла из-за неправильно настроенного внешнего ключа в моей базе данных (и модели, созданной из БД).

У меня были таблицы:

Parent (1-1) Child (1-many) Grandchild

и таблица Grandchild случайно получила внешний ключ до своего родителя (Child) и его прародителя (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 не допускает значения Null. Это означает, что он будет вставлен с 0, и EF будет жаловаться на Insert, а не при миграции - хотя правда в том, что после запуска миграции у вас есть таблица, в которую EF никогда не позволит вам вставить.

Единственный способ вернуть рекурсивную ссылку к той же таблице - сделать рекурсивную ссылку обнуляемой:

public class Location
{
    public int Id { get; set; }
    ...
    public Location ParentLocation { get; set; }
    [ForeignKey("ParentLocation")]
    public int? ParentLocationId { get; set; }
}

Обратите внимание на расширение ?после int.

Крис Москини
источник
Разве старая версия (не допускающая значения NULL) не будет работать, если вы выполните SaveChanges в родительском местоположении перед созданием дочернего местоположения?
Андре Копс
1
Игнорируйте выше, я идиот. Вы не сможете создать первую локацию.
Андре Копс
Проблема была в мощности. Верный.
aclalex
0

Для тех, кто ищет это исключение:
в моем случае не удалось установить необходимое свойство навигации.

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's Question collection
Р. Солсбери
источник
0

у меня была такая же проблема. с приведенным ниже сценарием, решенным для меня. я думаю, вы должны изменить свой код, например, ниже:

var insertedRoute =routeRep.Insert(route);
.....
insertedRoute.LocationInRoute = new List<LocationInRoute>();
for(....){
    var lInRoute = new LocationInRoute(){
    ....
    Route=insertedRoute;
}

insertedRoute.LocationInRoute.Add(lInRoute );
}
Ахмадреза Фаррохнеджад
источник