141

I'm having some issues with grabbing a list of type "RhsTruck" through Linq and getting them to display.

RhsTruck just has properites Make, Model, Serial etc... RhsCustomer has properties CustomerName, CustomerAddress, etc...

I keep getting the error "Sequence contains more than one element" (of type InvalidOperationException). Any ideas? Am I approaching this the wrong way?

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();   
}
SandRock
  • 5,276
  • 3
  • 30
  • 49
  • 1
    Use **take<>**, same with SQL **Top()** aggregate function, `.Take(1).SingleOrDefault();` – Thein Sep 15 '13 at 07:32

5 Answers5

309

The problem is that you are using SingleOrDefault. This method will only succeed when the collections contains exactly 0 or 1 element. I believe you are looking for FirstOrDefault which will succeed no matter how many elements are in the collection.

Cᴏʀʏ
  • 105,112
  • 20
  • 162
  • 194
JaredPar
  • 733,204
  • 149
  • 1,241
  • 1,454
  • 9
    Calvin, in that case you should accept this answer as a solution – Dejan Milicic Oct 04 '10 at 10:43
  • 26
    -1 *"The problem is you are using SingleOrDefault"* - from what I can gather the OP is looking for a customer id which (I assume) should be unique, therefore, `SingleOrDefault` is actually more appropriate than `FirstOrDefault`. Also, this has actually raised a more serious problem with the OP's database design as it shows that it is possible to add 2 customers with the same ID! – James Dec 27 '10 at 20:16
  • 30
    @James, the OP stated that my answer was correct and the exception clearly states the collection has more than one element which prevents `SingleOrDefault` from ever working. True it *may* be possible to have a better database design here but that seems more appropriate as a comment on the OP and not a -1 on an answer. – JaredPar Dec 28 '10 at 00:48
  • 9
    IMO the underlying problem is ultimately DB design as it shows that 2 unique customer ID's can be added to the database. `SingleOrDefault` is throwing an exception because there is an inconsistency between what the method expects and what it is finding. So although your answer stops the exception, for me, it does not actually solve the problem it's more of a "get out of jail free" card hence the -1. – James Dec 28 '10 at 13:01
  • 1
    I think you both made very astute observations. I had a similar issue due to a change of design, where something which was once unique become non-unique thus requiring this exact modification. I agree with James that refactoring the design and sticking to SingleOrDefault would be a better longterm approach however. – hydrogen May 02 '12 at 01:17
  • 2
    This is misleading! The use for `SingleOrDefault` then falls into when you expect a collection to have 0 or 1 items and you want to check this happens every time... – Achilles Apr 18 '13 at 15:52
  • This method will only succeed when 0 or 1 items *match on the lambda passed to SingleOrDefault*. It has nothing to do with the size of the collection that calls `SingleOrDefault`. – Millie Smith Mar 20 '15 at 15:49
29

SingleOrDefault method throws an Exception if there is more than one element in the sequence.

Apparently, your query in GetCustomer is finding more than one match. So you will either need to refine your query or, most likely, check your data to see why you're getting multiple results for a given customer number.

Fernando Carvalhosa
  • 1,098
  • 1
  • 15
  • 23
Mehmet Aras
  • 5,284
  • 1
  • 25
  • 32
19
Use FirstOrDefault insted of SingleOrDefault..

SingleOrDefault returns a SINGLE element or null if no element is found. If 2 elements are found in your Enumerable then it throws the exception you are seeing

FirstOrDefault returns the FIRST element it finds or null if no element is found. so if there are 2 elements that match your predicate the second one is ignored

   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);
4

FYI you can also get this error if EF Migrations tries to run with no Db configured, for example in a Test Project.

Chased this for hours before I figured out that it was erroring on a query, but, not because of the query but because it was when Migrations kicked in to try to create the Db.

Chris Moschini
  • 36,764
  • 19
  • 160
  • 190
3

As @Mehmet is pointing out, if your result is returning more then 1 elerment then you need to look into you data as i suspect that its not by design that you have customers sharing a customernumber.

But to the point i wanted to give you a quick overview.

//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();

for more Linq expressions have a look at System.Linq.Expressions

Martin Sax
  • 1,122
  • 7
  • 11