Varias entidades agregadas pueden tener la misma clave principal

81

Aquí está mi modelo de 3 entidades: Ruta, Ubicación y LocationInRoute.
modelo

el siguiente método falla y obtiene una excepción cuando lo confirma:

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

Al hacer:

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

Tengo:

No se puede determinar el final principal de la relación 'SimTaskModel.FK_T_STF_SUB_LOCATION_IN_ROUTE_T_STF_LOCATION_location_id'. Varias entidades agregadas pueden tener la misma clave principal.

Al dividir la confirmación e insertarla en los métodos, funciona:

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

Me gustaría llamar a commit una vez y fuera del método. ¿Por qué falla en el primer ejemplo y qué significa esta excepción?

Naor
fuente
9
@Ladislav Mrnka: No tengo jefe y este es un proyecto mío. Realmente no sé de dónde tiene la impresión de que pregunto inmediatamente en SO. No eres el único que usa la computadora todo el día. ¿Consultoría gratis? ¿Alguien da garantía a alguien por sus respuestas? Creo que este es un foro donde se pueden hacer preguntas aquí y esto es lo que estoy haciendo. Tengo muchas preguntas y creo que hago una gran distancia de aprendizaje gracias a este foro y a gente como tú. La participación es una elección.
Naor
1
@Ladislav: Solo veo una pregunta razonablemente bien formulada, y el perfil del OP tampoco indica nada exagerado.
Henk Holterman
¿Está utilizando el mismo ObjectContext en todo el alcance de la operación o cada nuevo Repositorio tendrá su propio ObjectContext?
Akash Kava
@Akash Kava: estoy usando el mismo ObjectContext.
Naor

Respuestas:

144

El error es causado por un ID de clave externa (a diferencia de una referencia) que no se puede resolver. En su caso, tiene LocationInRole que hace referencia a una ubicación con un ID de 0. Hay varias ubicaciones con este ID.

A las ubicaciones aún no se les ha asignado una identificación porque aún no se han guardado en la base de datos que es cuando se genera la identificación. En su segundo ejemplo, las ubicaciones se guardan antes de que se acceda a sus ID, por lo que esto funciona.

No podrá confiar en los ID de ubicación para definir las relaciones si desea Guardar cambios solo más adelante.

Intercambia la siguiente línea ...

LocationId = locations[i].Id

...para esto...

Location = locations[i]

Las relaciones se basarán en referencias de objetos que no dependen de los LocationID.

Scott Munro
fuente
¿Alguno de ustedes puede echar un vistazo a mi publicación y decirme cómo puedo solucionarlo? Tengo el mismo problema: stackoverflow.com/questions/26783934/… ¡Se lo agradezco!
duxfox
Recibo este error cuando implemento para probar env ... ¿no hay alguna idea en dev environemnet?
Taran
@Taran Si el código es idéntico y está usando el mismo proceso para probar en ambos entornos (verificaría estos puntos), entonces esto parece extraño. ¿Quizás está agregando solo una ubicación (siguiendo con el ejemplo aquí) en dev? Intente agregar al menos dos.
Scott Munro
Mi problema puede ser una variación de este. Estaba agregando los objetos secundarios a la colección de los padres sin establecer explícitamente la propiedad de los padres en los niños. Esperaba que EF resolviera esto, pero recibí el mismo error que el OP hasta que establecí explícitamente la propiedad principal en el niño. Espero que esto ayude a alguien.
Eric H
4

En caso de que esto sea de alguna utilidad para futuros lectores, en mi caso, este error se debió a una clave externa configurada incorrectamente en mi base de datos (y el modelo generado desde la base de datos).

Tenía mesas:

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

y la tabla de nietos había recibido inadvertidamente una clave externa para su padre (hijo) y su abuelo (padre). Al guardar varias entidades principales de nuevas, recibí este error. La solución ha sido corregir la clave externa.

Arrozal
fuente
En mi caso, había configurado (estúpidamente) mi Tabla base de clave principal igual que mi Tabla base de clave externa y mi Columna de clave principal igual que mi Columna de clave externa inclina la cabeza avergonzada Espero que esto ayude a alguien ...
Dave
3

Habiendo encontrado el mismo error, sospecho que el problema real era la definición de Ubicación. En pocas palabras, en EF Code First, apuesto a que se ve así:

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

En otras palabras, en la pregunta, ParentLocation / ParentLocationId son una referencia recursiva a esta tabla.

ParentLocationId no admite nulos. Eso significa que se insertará con un 0, y EF se quejará en Insert, en lugar de cuando migre, aunque la verdad es que una vez que se ejecuta la migración, tiene una tabla en la que EF nunca le permitirá insertar.

La única forma de hacer una referencia recursiva a la misma tabla funciona es hacer que la referencia recursiva sea anulable:

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

Tenga en cuenta el ?después del int.

Chris Moschini
fuente
¿No funcionaría la versión anterior (que no admite nulos) si realiza SaveChanges en la ubicación principal antes de crear la ubicación secundaria?
André Kops
1
Ignora arriba, soy un idiota. No podrías crear la primera ubicación.
André Kops
La cardinalidad era el problema. Correcto.
aclalex
0

Para aquellos que buscan esta excepción:
en mi caso, no se pudo establecer una propiedad de navegación requerida.

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

Tuve el mismo problema. con el siguiente escenario resuelto para mí. Creo que debe cambiar su código como se muestra a continuación:

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

insertedRoute.LocationInRoute.Add(lInRoute );
}
Ahmadreza Farrokhnejad
fuente