Sekwencja zawiera więcej niż jeden element

110

Mam pewne problemy z przechwyceniem listy typu „RhsTruck” przez Linq i wyświetleniem ich.

RhsTruck ma tylko odpowiednie cechy Marka, model, numer seryjny itp ... RhsCustomer ma właściwości CustomerName, CustomerAddress itp ...

Ciągle otrzymuję błąd „Sekwencja zawiera więcej niż jeden element”. Jakieś pomysły? Czy podchodzę do tego w niewłaściwy sposób?

public RhsCustomer GetCustomer(string customerNumber)
{
    using (RhsEbsDataContext context = new RhsEbsDataContext() )
    {
        RhsCustomer rc = (from x in context.custmasts
                          where x.kcustnum == customerNumber
                          select new RhsCustomer()
                        {
                            CustomerName = x.custname,
                            CustomerAddress = x.custadd + ", " + x.custcity
                            CustomerPhone = x.custphone,
                            CustomerFax = x.custfax
                        }).SingleOrDefault();
        return rc;
    }
}

public List<RhsTruck> GetEquipmentOwned(RhsCustomer cust)
{
    using (RhsEbsDataContext context = new RhsEbsDataContext())
    {
        var trucks = (from m in context.mkpops
                      join c in context.custmasts
                        on m.kcustnum equals c.kcustnum
                      where m.kcustnum == cust.CustomerNumber
                      select new RhsTruck
                    {
                        Make = m.kmfg,
                        Model = m.kmodel,
                        Serial = m.kserialnum,
                        EquipID = m.kserialno1,
                        IsRental = false
                    }).ToList();
        return trucks;
    }
}

protected void Page_Load(object sender, EventArgs e)
{
    string testCustNum = Page.Request.QueryString["custnum"].ToString();

    RhsCustomerRepository rcrep = new RhsCustomerRepository();
    RhsCustomer rc = rcrep.GetCustomer(testCustNum);
    List<RhsTruck> trucks = rcrep.GetEquipmentOwned(rc);

    // I want to display the List into a Gridview w/auto-generated columns
    GridViewTrucks.DataSource = trucks;
    GridViewTrucks.DataBind();   
}
Owen Blacker
źródło
1
Użyj funkcji take <> , tak samo jak funkcji agregującej SQL Top () ,.Take(1).SingleOrDefault();
Thein

Odpowiedzi:

254

Problem w tym, że używasz SingleOrDefault. Ta metoda powiedzie się tylko wtedy, gdy kolekcje zawierają dokładnie 0 lub 1 element. Wierzę, że szukasz tego, FirstOrDefaultktóry odniesie sukces bez względu na ilość elementów w kolekcji.

JaredPar
źródło
8
Calvin, w takim przypadku powinieneś zaakceptować tę odpowiedź jako rozwiązanie
Dejan Milicic
24
-1 „Problem polega na tym, że używasz SingleOrDefault” - z tego, co mogę stwierdzić, OP szuka identyfikatora klienta, który (jak zakładam) powinien być unikalny, a zatem SingleOrDefaultjest bardziej odpowiedni niż FirstOrDefault. Ponadto spowodowało to poważniejszy problem z projektem bazy danych PO, ponieważ pokazuje, że możliwe jest dodanie 2 klientów o tym samym identyfikatorze!
James
27
@James, OP stwierdził, że moja odpowiedź była poprawna, a wyjątek wyraźnie stwierdza, że ​​kolekcja ma więcej niż jeden element, który uniemożliwia jej SingleOrDefaultdziałanie. Prawdą jest, że można tu mieć lepszy projekt bazy danych, ale wydaje się to bardziej odpowiednie jako komentarz do PO, a nie -1 do odpowiedzi.
JaredPar
9
IMO podstawowym problemem jest ostatecznie projekt bazy danych, ponieważ pokazuje, że do bazy danych można dodać 2 unikalne identyfikatory klientów. SingleOrDefaultzgłasza wyjątek, ponieważ istnieje niespójność między tym, czego oczekuje metoda, a tym, co znajduje. Więc chociaż twoja odpowiedź zatrzymuje wyjątek, dla mnie tak naprawdę nie rozwiązuje problemu, jest to raczej karta „wyjdź z więzienia bez więzienia”, stąd -1.
James
2
To jest mylące! Zastosowanie SingleOrDefaultto przypada wtedy, gdy spodziewasz się, że kolekcja zawiera 0 lub 1 elementów i chcesz sprawdzić, czy dzieje się to za każdym razem ...
Achilles
23

SingleOrDefaultmetoda zgłasza, Exceptionjeśli w sekwencji jest więcej niż jeden element.

Wygląda na to, że Twoje zapytanie w programie GetCustomerznajduje więcej niż jedno dopasowanie. Musisz więc albo zawęzić zapytanie, albo, najprawdopodobniej, sprawdzić dane, aby zobaczyć, dlaczego otrzymujesz wiele wyników dla danego numeru klienta.

Mehmet Aras
źródło
5
Use FirstOrDefault insted of SingleOrDefault..

SingleOrDefault zwraca SINGLE element lub null, jeśli nie zostanie znaleziony żaden element. Jeśli w twoim Enumerable znajdują się 2 elementy, zgłasza wyjątek, który widzisz

FirstOrDefault zwraca PIERWSZY znaleziony element lub wartość null, jeśli nie zostanie znaleziony żaden element. więc jeśli istnieją 2 elementy, które pasują do twojego orzeczenia, drugi jest ignorowany

   public int GetPackage(int id,int emp)
           {
             int getpackages=Convert.ToInt32(EmployerSubscriptionPackage.GetAllData().Where(x
   => x.SubscriptionPackageID ==`enter code here` id && x.EmployerID==emp ).FirstOrDefault().ID);
               return getpackages;
           }

 1. var EmployerId = Convert.ToInt32(Session["EmployerId"]);
               var getpackage = GetPackage(employerSubscription.ID, EmployerId);
Muhammad Armaghan
źródło
1

FYI, można również uzyskać ten błąd, jeśli migracje EF próbuje uruchomić bez skonfigurowanej bazy danych, na przykład w projekcie testowym.

Goniłem za tym przez wiele godzin, zanim zorientowałem się, że to błąd w zapytaniu, ale nie z powodu zapytania, ale dlatego, że wtedy włączyły się migracje, aby spróbować utworzyć bazę danych.

Chris Moschini
źródło
0

Jak wskazuje @Mehmet, jeśli Twój wynik zwraca więcej niż 1 elerment, musisz przyjrzeć się swoim danym, ponieważ podejrzewam, że nie jest to zgodne z projektem, że masz klientów udostępniających niestandardowy numer.

Ale do tego stopnia chciałem dać ci szybki przegląd.

//success on 0 or 1 in the list, returns dafault() of whats in the list if 0
list.SingleOrDefault();
//success on 1 and only 1 in the list
list.Single();

//success on 0-n, returns first element in the list or default() if 0 
list.FirstOrDefault();
//success 1-n, returns the first element in the list
list.First();

//success on 0-n, returns first element in the list or default() if 0 
list.LastOrDefault();
//success 1-n, returns the last element in the list
list.Last();

więcej wyrażeń Linq znajdziesz w System.Linq.Expressions

Martin Sax
źródło