Wiele dodanych jednostek może mieć ten sam klucz podstawowy

81

Oto mój model 3 jednostek: Route, Location i LocationInRoute.
Model

poniższa metoda zawodzi i otrzyma wyjątek po jej zatwierdzeniu:

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

Robiąc:

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

Mam:

Nie można określić głównego końca relacji „SimTaskModel.FK_T_STF_SUB_LOCATION_IN_ROUTE_T_STF_LOCATION_location_id”. Wiele dodanych jednostek może mieć ten sam klucz podstawowy.

Podczas dzielenia zatwierdzenia i wstawiania do methos - działa:

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

Chciałbym wywołać commit raz i poza tą metodą. Dlaczego zawodzi w pierwszym przykładzie i co oznacza ten wyjątek?

Naor
źródło
9
@Ladislav Mrnka: Nie mam szefa i to jest mój projekt. Naprawdę nie wiem, skąd masz wrażenie, że od razu pytam na SO. Nie jesteś jedynym, który używa komputera przez cały dzień. Doradztwo za darmo? czy ktoś udziela komukolwiek gwarancji za swoje odpowiedzi? Myślę, że jest to forum, na którym można zadawać pytania i to właśnie robię. Mam wiele pytań i wierzę, że dzięki temu forum i ludziom takim jak Ty uczę się na odległość. Udział jest wyborem.
Naor
1
@Ladislav: Widzę tylko dość dobrze zadane pytanie, a profil OP również nie wskazuje niczego przesadnego.
Henk Holterman
Czy używasz tego samego ObjectContext w całym zakresie operacji, czy każde nowe repozytorium będzie miało własny ObjectContext?
Akash Kava
@Akash Kava: Używam tego samego ObjectContext.
Naor

Odpowiedzi:

144

Błąd jest spowodowany identyfikatorem klucza obcego (w przeciwieństwie do odwołania), którego nie można rozwiązać. W Twoim przypadku masz LocationInRole, która odwołuje się do lokalizacji o identyfikatorze 0. Istnieje wiele lokalizacji o tym identyfikatorze.

Lokacjom nie przypisano jeszcze identyfikatora, ponieważ nie zostały one jeszcze zapisane w bazie danych, w której jest generowany identyfikator. W drugim przykładzie lokalizacje są zapisywane przed uzyskaniem dostępu do ich identyfikatorów, dlatego to działa.

Nie będziesz mógł polegać na identyfikatorach lokalizacji w celu zdefiniowania relacji, jeśli chcesz zapisać zmiany dopiero później.

Zamień następującą linię ...

LocationId = locations[i].Id

...dla tego...

Location = locations[i]

Relacje będą wówczas oparte na odwołaniach do obiektów, które nie są zależne od identyfikatorów lokalizacji.

Scott Munro
źródło
czy któryś z was może rzucić okiem na mój post i powiedzieć, jak mogę to naprawić, pojawia się ten sam problem: stackoverflow.com/questions/26783934/… Doceniam to!
duxfox
Otrzymuję ten błąd podczas wdrażania w celu przetestowania środowiska ... a nie w środowisku deweloperskim żadnych pomysłów?
Taran
@Taran Jeśli kod jest identyczny i używasz tego samego procesu do testowania w obu środowiskach (sprawdziłbym te punkty), wydaje się to dziwne. Może dodajesz tylko jedną lokalizację (zgodnie z przykładem tutaj) w programie dev? Spróbuj dodać co najmniej dwa.
Scott Munro
Mój problem może być odmianą tego. Dodawałem obiekty podrzędne do kolekcji rodzica bez jawnego ustawiania właściwości rodzica na dzieciach. Spodziewałem się, że EF rozwiąże ten problem, ale otrzymałem ten sam błąd co OP, dopóki wyraźnie nie ustawię właściwości nadrzędnej na dziecku. Mam nadzieję, że to komuś pomoże.
Eric H
4

W przypadku, gdy jest to przydatne dla przyszłych czytelników, w moim przypadku ten błąd był spowodowany nieprawidłowo skonfigurowanym kluczem obcym w mojej bazie danych (i modelu wygenerowanym z bazy danych).

Miałem tabele:

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

a tabela wnuków przypadkowo otrzymała klucz obcy do swojego rodzica (dziecka) i dziadka (rodzica). Podczas zapisywania wielu podmiotów nadrzędnych z nowego, otrzymałem ten błąd. Poprawka polegała na poprawieniu klucza obcego.

Paddy
źródło
W moim przypadku (głupio) ustawiłem moją podstawową tabelę kluczy podstawowych tak samo, jak moją tabelę bazową kluczy obcych, a kolumnę kluczy podstawowych tak samo, jak moja kolumna klucza obcego pochyla głowę ze wstydem Mam nadzieję, że to komuś pomoże ...
Dave
3

Po napotkaniu tego samego błędu podejrzewam, że rzeczywistym problemem była definicja lokalizacji. Mówiąc prościej, w EF Code First założę się, że wyglądało to tak:

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

Innymi słowy, w pytaniu ParentLocation / ParentLocationId są rekursywnymi odwołaniami do tej tabeli.

ParentLocationId nie dopuszcza wartości Nullable. Oznacza to, że zostanie wstawiony z 0, a EF będzie narzekać na wstawianie, a nie podczas migracji - nawet jeśli prawda jest taka, że ​​po uruchomieniu migracji masz tabelę EF, która nigdy nie pozwoli ci wstawić.

Jedynym sposobem, aby odwołanie rekurencyjne z powrotem do tej samej tabeli działało, jest ustawienie wartości nullable referencji rekurencyjnej:

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

Zwróć uwagę na ?po int.

Chris Moschini
źródło
Czy stara (niezawierająca wartości null) wersja nie zadziała, jeśli wykonasz SaveChanges w lokalizacji nadrzędnej przed utworzeniem lokalizacji podrzędnej?
André Kops
1
Zignoruj ​​powyżej, jestem idiotą. Nie byłbyś w stanie stworzyć pierwszej lokalizacji.
André Kops
Problemem była liczność. Poprawny.
aclalex
0

Dla tych, którzy szukają tego wyjątku: w
moim przypadku nie udało się ustawić wymaganej właściwości nawigacji.

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
źródło
0

miałem ten sam problem. z poniższym scenariuszem rozwiązanym dla mnie. myślę, że musisz zmienić swój kod, taki jak poniżej:

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

insertedRoute.LocationInRoute.Add(lInRoute );
}
Ahmadreza Farrokhnejad
źródło