Ten błąd występuje, gdy EF nie może zezwolić na PK dla swojej istocie. W większości przypadków dla prostych bytów umowy EF mogą ustalać PK, ale w twoim przypadku używasz klucz złożony, dlatego należy go skonfigurować. W zależności od tego, jak zbieracie swoje istocie, można to zrobić w:
- EDMX
- w DbContext.Tworzenie modelu
- za pomocą
EntityTypeConfiguration
deklaracja
- korzystanie z atrybutów wewnątrz samej encji
Ponieważ nie wiemy, jak dostosować swoje encji, możesz to sprawdzić, jak powód, wykorzystując podejście atrybutów w swojej istocie jako test. Jeśli używasz EDMX, klasy encji będą generowane, więc chcesz wymienić to konfiguracją w EDMX. (Nie mogę ci w tym pomóc, bo nie używam te cholerne rzeczy :D )
U ciebie pewnie będzie coś takiego:
public class CarRentalFields
{
[Column("start_day")]
public DateTime StartDay { get; set; }
[Column("return_date")]
public DateTime ReturnDate { get; set; }
[Column("user_id")]
public int UserId { get; set; }
[Column("car_number")]
public DateTime CarNumber { get; set; }
// ... more columns...
}
Być może masz nawet [Key]
atrybut w jednym z tych pól, na przykład, numer porządkowy. Jeśli w istocie jest wyświetlany PK, problem polega na tym, że nie był dość specyficzny, aby jednoznacznie zidentyfikować wiersz. Kiedy EF przechodzi do aktualizacji jednej istocie, on sprawdza i czeka na aktualizacje tylko jednego wiersza w tabeli. To doprowadzi do tego, że będzie narażona na więcej niż jeden wiersz, więc to nie uda.
Dodaj atrybuty [Key]
kolejność kolumn, aby został rozpoznany jako klucz złożony.
public class CarRentalFields
{
[Key, Column(Name="start_day", Order=1)]
public DateTime StartDay { get; set; }
[Key, Column(Name="return_date", Order=2)]
public DateTime ReturnDate { get; set; }
[Key, Column(Name="user_id", Order=3)]
public int UserId { get; set; }
[Key, Column(Name="car_number", Order=4)]
public DateTime CarNumber { get; set; }
// ... more columns...
}
Pod warunkiem, że te 4 kolumny gwarantowane są wyjątkowym ograniczeniem dla tabeli, EF będzie zadowolony, jeśli podczas tworzenia instrukcji języka SQL UPDATE będzie przeglad zrobiono tylko jeden wiersz.
Jeszcze raz należy zwrócić uwagę, że jeśli to działa i używasz EDMX, trzeba będzie zobaczyć i zmienić wyświetlanie EDMX, aby wprowadzić odpowiednie zmiany, ponieważ ta klasa encji może być przywrócony, tracąc swoje dodatkowe atrybuty. (Zakładam, że wygenerowane klasy encji z EDMX mają tytuł komentarza, ostrzeżenia o tym, że to wygenerowane klasy, więc jest to wskaźnik, na który należy zwrócić uwagę.)
Odśwież:
Moje główne podejrzenie w tym będzie polegała na tym, że w tabeli w rzeczywistości nie jest zdefiniowany odpowiedni PK, albo jest używana inna kombinacja PK, albo, co bardziej prawdopodobne, nie ma PK, biorąc pod uwagę naturę tych pól. EF może pracować z tabelami, dla których nie określono PK, ale do tego potrzebne jest określenie klucza, zapewniające unikalną identyfikację rekordów. Błąd, który widzisz, występuje, gdy jest to definicja klucza za mało jest unikalna. (tzn. Jeśli aktualizujesz car 1 i wybierz linię, która ma:
car_number = 1, start_day = 2021-11-21, return_day = 2021-11-22, identyfikator użytkownika = 0 Problem w tym, że w bazie danych więcej niż jeden wiersz zawiera taką kombinację. Jeśli w bazie danych, którą wybrałeś, nie więcej niż jeden zbiega się wiersza, to twoja aplikacja prawie na pewno wskazuje na innej bazy danych niż sprawdzasz.
Co można zrobić, aby upewnić się w tym:
- uzyskaj ciąg połączenia w czasie wykonywania i zobacz, czy odpowiada ona bazie danych, którą sprawdzasz:
Przed wykonaniem kwerendy dodaj następujący:
// EF6
var connectionString = db.Database.Connection.ConnectionString;
// EF Core 5
var connectionString = db.Database.GetConnectionString();
- Spójrz na dane, które faktycznie wniosek:
.
var cars = db.CarRentalFields.Where(carNum =>
(carNum.CarNumber == carNumber1 && carNum.ActualReturnDate == null)).ToList();
Chociaż to zapytanie może zwrócić tylko 1 wpis, to nie jest przyczyną problemu. Czego potrzebujesz, to numer, data rozpoczęcia, data zwrotu i identyfikator użytkownika dla tego konta:
var car = db.CarRentalFields
.Where(carNum => carNum.CarNumber == carNumber1
&& carNum.ActualReturnDate == null)
.Select(x => new
{
x.CarNumber,
x.StartDay,
x.ReturnDate,
x.UserId
}).Single(); // Expect our 1 record here...
var cars = db.CarRentalFields
.Where(x => x.CarNumber == car.CarNumber
&& x.StartDay == car.StartDay
&& x.ReturnDate == car.ReturnDate
&& x.UserId == car.UserId)
.ToList(); // Get rows that match our returned Key fields.
Te żądania wybierają rzekome wartości PK dla nagrywania samochodu, który chcesz zaktualizować, a następnie wykonują wyszukiwanie samochodów do mapowania kont z oczekiwanymi kluczowymi polami. Ja bym postawił na to, że choć górny zapytanie zwraca 1 wpis, dolny zapytanie zwraca dwa wiersze, to jest, choć tylko 1 wpis ma wartość #null ActualReturnDate, twój klucz nie wystarczy wyjątkowy dla zawartości tej tabeli.