web-dev-qa-db-fra.com

Plusieurs entités peuvent avoir la même clé primaire

Voici mon modèle de 3 entités: Route, Location et LocationInRoute.
model

la méthode suivante échoue et obtient une exception lors de la validation:

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

En faisant:

InsertRouteIfNotExists(companyId, locations);
UnitOfWork.Commit();

J'ai eu:

Impossible de déterminer l'extrémité principale de la relation 'SimTaskModel.FK_T_STF_SUB_LOCATION_IN_ROUTE_T_STF_LOCATION_location_id'. Plusieurs entités ajoutées peuvent avoir la même clé primaire.

Lorsque vous divisez le commit et que vous l'insérez dans le methos - cela fonctionne:

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

Je voudrais appeler commit une fois et en dehors de la méthode. Pourquoi cela échoue-t-il dans le premier exemple et que signifie cette exception?

69
Naor

L'erreur est provoquée par un ID de clé étrangère (par opposition à une référence) qui ne peut pas être résolu. Dans votre cas, vous avez un LocationInRole qui référence un emplacement avec un ID égal à 0. Il existe plusieurs emplacements avec cet ID.

Aucun ID n'a encore été attribué aux emplacements car ils n'ont pas encore été enregistrés dans la base de données, date à laquelle l'ID est généré. Dans votre deuxième exemple, les emplacements sont enregistrés avant que leurs identifiants ne soient consultés, raison pour laquelle cela fonctionne.

Vous ne pourrez pas vous fier aux identifiants d'emplacement pour définir les relations si vous souhaitez uniquement enregistrer des modifications ultérieurement.

Échangez la ligne suivante ...

LocationId = locations[i].Id

...pour ça...

Location = locations[i]

Les relations seront ensuite basées sur des références d'objet qui ne dépendent pas des identificateurs de localisation.

132
Scott Munro

Au cas où cela serait utile aux futurs lecteurs, dans mon cas, cette erreur était due à une clé étrangère mal configurée dans ma base de données (et au modèle généré à partir de la base de données).

J'avais des tables:

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

et la table des petits-enfants a reçu par inadvertance une clé étrangère jusqu'à son parent (enfant) et à son grand-parent (parent). Lors de la sauvegarde de plusieurs entités parent de nouveau, j'ai reçu cette erreur. Correction a été de corriger la clé étrangère.

4
Paddy

Ayant rencontré la même erreur, je soupçonne fortement que le problème réel était la définition de «lieu». En termes simples, dans EF Code, je parie que cela ressemble à ceci:

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

En d'autres termes, dans la question, ParentLocation/ParentLocationId est une référence récursive vers cette table.

Le ParentLocationId n'est pas Nullable. Cela signifie qu'il sera inséré avec un 0 et EF se plaindra sur Insérer, plutôt que lors de la migration - même si la vérité est qu'une fois que Migration est en cours d'exécution, vous disposez d'une table dans laquelle EF ne vous laissera jamais insérer.

La seule façon de faire une référence récursive sur le même travail de table est de rendre la référence récursive nullable:

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

Notez le ? après la int.

1
Chris Moschini

j'ai eu le même problème. avec le scénario ci-dessous résolu pour moi ... je pense que vous devez changer votre code

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

insertedRoute.LocationInRoute.Add(lInRoute );
}
0

Pour ceux qui recherchent cette exception:
Dans mon cas, il ne parvenait pas à définir une propriété de navigation requise.

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
R. Salisbury