How to get the last object in a generic list? - c#

I am passing a populated SelectList to my View and I want to default the selected value to the last record in the list. So far I've got:
IQueryable<BuildNumber> projBuildNos = tcRepository.GetProjectBuildNos();
BuildList = new SelectList(projBuildNos, "ID", "value", projBuildNos.Last().ID);
I get an exception saying "the query operator 'Last' is not supported but I can't seem to get at the last record in my generic list and can't find any examples.
This code is from a 'form view model' passing select lists from my repository back to the view and I think I should be performing this here. However I'm new to generics/mvc/linq so am happy for alternative suggestions.
Thanks in advance for any help. Please let me know if you want any more info.

Are you already going to suck all the results from your query into memory? If so, I suggest you execute your query and get the result into a list:
List<BuildNumber> projBuildNos = tcRepository.GetProjectBuildNos().ToList();
BuildList = new SelectList(projBuildNos, "ID", "value", projBuildNos.Last().ID);
Otherwise you could easily end up with a workaround which executes the query twice - possibly giving inconsistent results.

I found this on MSDN:
Queryable.LastOrDefault -- Returns the last element in a sequence, or a default value if the sequence contains no elements.

Maybe this?
projBuildNos.Reverse().First().ID

Related

Linq: Get Item which is in a list which also is in a list

I'm currently trying to get a list of products which are in a list of stores but only if the product name is the same.
I always get 0 Items back.
I tried to solve to problem using two different approaches, which are below.
//First Approach, return 0
var stores= Store.ReadAll().Where(prods =>
prods.Products.Contains(product))
//Second Approach, doesn't compile but it shows what i wan't to do.
var stores= Store.ReadAll().Where(prods =>
prods.Products.Where(p => p.ProductName == productName));
Help appreciated :)
What you are looking for is Any instead of Where:
var products = Store.ReadAll().Where(prods => prods.Products.Any(p => p.ProductName == productName));
So i partly solved my problem and apparently, it wasn't really a linq problem, it was a database problem. I have a List of objects which contains a list of objects, which gives me a M:M relation. But the .net Entity Framework didn't regonize when i changed the list using list.add(item). So my list was always empty.
But anyway, thanks for the help ! :)
Assuming stores is an IEnumerable type, then following should work. It's important to pass the second parameter to Contains method, so comparision is not case sensitive, else it will return nothing if case is different between product names.
I have assumed in my answer that Store type has a property called Products of List< string > type.
var matchingStores = stores.Where(s=> s.Products.Contains(productName,
StringComparer.OrdinalIgnoreCase));

How to use returned linq variable?

I've decided to take a quick look into the LINQ side of things, as opposed to just using a straight up foreach loop, but i'm having some trouble getting it to work, mainly due to datatypes i believe.
So i've got this, so far;
var selectedSiteType = from sites in siteTypeList
where sites.SiteTypeID == temp
select sites;
siteTypeList is a list of SiteTypes. I'm trying to find a particular one (Which i've denounced with variable "temp".
How do i then use this selected SiteType AS a SiteType? When i try and pass "selectedSiteType" through to another function, like so;
mSiteTypeSub.EditSitetype(selectedSiteType);
note: I tried with providing an index, as if selectedSiteType was a list / Array, but that didnt work either, i get the following error:
Argument 1: cannot convert from
'System.Collections.Generic.IEnumerable<DeviceManager_take_2.SiteType>' to
'DeviceManager_take_2.SiteType'
Am i missing something? perhaps a cast of some kind? Like i said i'm new to this and am struggling to get my head around this. Chances are i've got the whole concept wrong and bingbangbosh i've made a fool of myself!
Cheers in advance.
Use First / FirstOrDefault / Single / SingleOrDefault to get an item of the particular type from the collection.
var value = selectedSiteType.First();
// returns the first item of the collection
var value = selectedSiteType.FirstOrDefault();
// returns the first item of the collection or null if none exists
var value = selectedSiteType.Single();
// returns the only one item of the collection, exception is thrown if more then one exists
var value = selectedSiteType.SingleOrDefault();
// returns the only item from the collection or null, if none exists. If the collection contains more than one item, an exception is thrown.
If your return type is a single:
var selectedSiteType = (from sites in siteTypeList
where sites.SiteTypeID == temp
select sites).SingleOrDefault();
If a list (potentially more than one item):
var selectedSiteType = (from sites in siteTypeList
where sites.SiteTypeID == temp
select sites).ToList();
It's the SingleOrDefault / ToList that you're missing from your query.
Shane,
I'm not going to improve on the previous answers. They were both correct. I am going to try and explain a little bit to you, so that you understand it in the future a bit better.
What happens, when you write a piece of code like:
var selectedSiteType = from sites in siteTypeList
where sites.SiteTypeID == temp
select sites;
you don't put the answer into the var (selectedSiteType), instead, you are creating an expression tree, that is evaluated ONLY when you actually use it (in a foreach, or by calling one of the methods (like .First(), .ToList(), SingleOrDefault(), etc).
The default return type of a from statement, is an IEnumerable<>, but if you call the .First() or .SingleOrDefault() (etc), you will dive into that IEnumerable<> and get a specific item.
I hope this helps you better understand what is going on.
Lemme know if I can add anything or if I got anything wrong.
Cheers,
Max

Shortest way to bind a certain amout of items from a list to a data source?

Anyone know how to select a certain amount of items in a List to bind to a DataSource? Basically I'm getting back 10 items (which I don't have control over) and I only need to show 5. Originally I was thinking of using a loop and adding 5 items to a new list but that seems like a lot of code. Is there an expression that I can use to select the first 5?
//Returns a List<DataItem>
MyDataListControl.DataSource = Helper.GetDataItems(); //<= Possible expression?
You may take a look at the Skip and Take LINQ extension methods. So in your case if you wanted to take only the first 5 elements of some IEnumerable<T>:
MyDataListControl.DataSource = Helper.GetDataItems().Take(5).ToList();
What about List's GetRange method? Have you tried that? I don't the internal workings of the method; whether it also creates a new list or not.
GetRange(int index, int count)
Here is the msdn link for it.
RemoveRange will probably be best as you won't have to instanciate a new list, unless that happens internally anyway.. Just make sure you're always getting 10 items or you'll potentially get an ArgumentOutOfRangeException.
list.RemoveRange(5, 5);
That should leave you with the first five items.

SingleResult<myObject> .ToList or .Count throws an InvalidCastException

I have a stored procedure GetTopRecords(). The sp returns the top 500 records.
I dragged the sp from the server explorer to the LinqtoSql designer surface.
I changed the return type to Record
In code I have:
var x = myDataContext.GetTopRecords();
var y = x.Count();
var z = x.ToList();
either one of the last two lines above throws an InvalidCastException
the type of x is
System.Data.Linq.SqlClient.SqlProvider.SingleResult<Record>
Why am I getting an exception? What am I doing wrong? Please help.
x is not a list, it is single element. As a single element you can't convert it to a list of more than one element or count how many elements it has.
As you say "I changed the return type to Record". Why did you do this?
Since this is L2S (Linq To SQL), I'm assuming it's using Lazy loading, which means that only when calling Count or ToList, which are of immediate execution, the query is performed.
That said, the problem should be related to what you are returning from the GetTopRecords method, which isn't compatible with the Record type.
You should use the L2S designer to check if the result of that method is correctly mapped to a Record data type. Meaning that Record should be a L2S mapped table and GetTopRecords should return data from that table.
Well,can you give details what GetTopRecords() .What it returns?do you need count of top records?
var x = i.ToList();
var y = x.Count();
then it will allowed,but when ever you use tolist() ToList iterates on the query and creates an instance of List<> initialized with all the results of the query.
which is same for count(),becuse count also need all records to perform counting.
the purpose of tolist is to prefilled data with result.
I think there there have been changes to the database schema since the linq to sql classes were generated. therefore there is a mismatch between the database table and the linq to Sql object representing it. therefore, the calls fail because the .NET cannot successfully cast from the db created object to the linq to sql object
I am not sure how I resolved this. But it is fixed now. If someone runs into this problem, please add a note here and I will look up my code and post the proper answer. While I thank the responders above, unfortunately, none of the answers above helped.

Finding location in list,

In Linq Get items higher then lastname I asked how to get people higher then a certain name.
However we use a datagrid with paging and when showing only people higher then lastname "Jan", there's no way to get back to people starting with AA.
So I was searching for a way to find out on what location the person would have been if following the normal order of selection?
hope this makes sense :)
[edit]
The query type is of EntityQuery.
[/edit]
So you want the index of first person record that matches your condition?
var index = query.TakeWhile(person => person.LastName.CompareTo(name) < 0).Count();
This counts the number of elements that match the condition and the returned value is the index of the first element that does not match with the condition.
Just note that you must negate the condition that you used with Where() as in this case TakeWhile() is used to get the items that you don't want.
Manage to solve this problem using the example given at
Getting to certain member using datapager and datagrid

Categories

Resources