foreach var x in ... with one result ASP.NET MVC RAZOR - c#

I have this code:
foreach (var parent in #CurrentPage.AncestorsOrSelf().OrderBy("Level").Skip(2).Take(1))
{
...
}
The #CurrentPage.AncestorsOrSelf().OrderBy("Level").Skip(2).Take(1) returns always one result, which is right. In this there is no sense to use foreach loop when the result is only one.
How can i store the result from #CurrentPage.AncestorsOrSelf().OrderBy("Level").Skip(2).Take(1) inside a variable?

You can call .First() or .FirstOrDefault() to get the actual object:
var value = #CurrentPage.AncestorsOrSelf().OrderBy("Level").Skip(2).First();
The difference between them is that First() will throw an exception if there are no items, while FirstOrDefault() will return default(T) (that is, null for reference types and e.g. 0 for ints etc).
Since you're extracting the first item, you don't need Take(1).

Related

Error: “Cannot implicitly convert type”

I am working on one project and I have a question regarding the converting type. I want to create simple search for my project, but it can't return result with this message :
Error 1 Cannot implicitly convert type 'System.Collections.Generic.List' to 'EmployeeDataAccess.TimeWorkMonthly'
public TimeWorkMonthly Get(int id)
{
using (EmployeeDbEntities Entities = new EmployeeDbEntities())
{
List<TimeWorkMonthly> persons = new List<TimeWorkMonthly>();
var result = Entities.TimeWorkMonthlies
.Where(e => e.KartNo == id)
.Select(e => e)
.ToList();
return result.ToList();
}
}
The return type of your method is TimeWorkMonthlies but inside the method body return List<TimeWorkMonthlies>
You should either
change your method return type to IEnumerable<TimeWorkMonthlies>
(You could use List<TimeWorkMonthlies> but using an interface to abstract a collection type is better for many reasons)
Use FirstOrDefault, First, SingleOrDefault or Single extension methods of IEnumerable if you aim to return only one element and you do not care about anything except for the first element
Which of those methods is better depends on your data and search criteria - i.e. whether you expect this ID to be unique or not.
From your semantics it looks like you're doing a sort of repository like ID lookup, so my guess would be solution 2) and using Single or SingleOrDefault
The last choice is how you want your program to behave if nothing is found by ID
If you want an exception, use Single
If you want a null use SingleOrDefault
In Summary, all you have to do is change your last line of code to
return result.Single();
(And ofcourse, you don't need a call to ToList() just before that)
Your method signature indicates you just want to return a single object. But you're returning a List of objects. Using .ToList() is not appropriate when you just want to return one object. There are four appropriate extension methods:
First - will return the first item from the collection and throw an exception if the collection is empty.
FirstOrDefault - will return the first item in the collection, or the default of the type if the collection is empty.
Single - if there is one item in the collection, it will return it. If there are no items in the collection an exception is thrown. If there are multiple items in the collection, an exception is thrown.
SingleOrDefault - if there is one item in the collection it will return it. If there are no items in the collection it will return the default value for the type. If there are multiple items in the collection it will thrown an exception.
Since you're searching by ID, you probably don't ever to expect to match two or more elements. So that rules out First and FirstOrDefault. You should use Single or SingleOrDefault depending on what you want the behavior to be if there is no item found that has the matching ID.
public TimeWorkMonthly Get(int id)
{
using (EmployeeDbEntities Entities = new EmployeeDbEntities())
{
var result = Entities.TimeWorkMonthlies.Where(e => e.KartNo == id).Single();
return result;
}
}
Note I eliminated the persons variable because you never did anything with it. And your usage of the .Select extension method was superflous since you just selected the same object already being iterated over. Select is for when you want to transform the object.
The problem is your qry only . If you want to convert it with Tolist() function you have to change your qry
like this
public TimeWorkMonthly Get(int id)
{
using (EmployeeDbEntities Entities = new EmployeeDbEntities())
{
var result = from x in Entities.TimeWorkMonthlies
Where x.KartNo == id
Select x;
return result.ToList();
}
}
You can now convert it to list by tolist() and use it according to your need.

Issue with query returning null before I can check for null

For an invite function, I have a screen with a single field on it that lets users enter a code they've been given elsewhere. I then check in the controller to see which record is associated to this code. But my issue is with the controller blowing up if the code is not the first type is checks for (type 1 or type 2). So here what I believe it should be like(this doesn't work), although I realize the sloppiness I'm just new to c#.
Record rec = db.Records.Where(u = u.Code1).First();
If (rec != null)
{
Other code...
Exit controller
}
Record rec2 = db.Records.Where(u = u.Code2).First();
If (rec2 != null)
{
Other code...
Exit controller
}
Return to view - codes are invalid.
I've attempted other versions where I put the object check within the if statement but there didn't work either. Any ideas?
Use FirstOrDefault not First. First throws an exception if the collection is empty, FirstOrDefault returns default(T) if the collection is empty.
And actually, there are overloads of First and FirstOrDefault that take a condition parameter, so you don't need to call Where
ex:
var x = numbers.FirstOrDefault(n => n % 2 == 1);
This will return the first odd number in the collection, or 0 if there are none that meet the condition (or none at all.)
As explained by the MSDN docs on IEnumerable.First, this method throws an exception if there are no elements in the sequence
The First(IEnumerable) method throws an exception if
source contains no elements. To instead return a default value when
the source sequence is empty, use the FirstOrDefault method
So you should write
Record rec = db.Records.FirstOrDefault(u => u.Code1);
if(rec == null)
{
....
}
Notice that the same condition used in the Where extension could be used directly with FirstOrDefault
By the way, it is unclear what your expression is. The lambda expression (syntax is => ) should return a boolean value so, or your Code1 is a boolean variable or something is wrong in that line
Use db.Records.Where(u => u.Code1).FirstOrDefault();
Check the comparison value (ex. u.code1) if it has this property from the model before binding to the query.

simple linq issue, but I can't figure it out

I have a simple LINQ issue. I'm trying to query a list and match a member ID with a lists ID and then return some text:
var IDtext = IDMethod(MembID);
var t = from ctext in cIDtext
where ctext.cid.ToString() == MembID
select cxt.C_ID;
ViewBag.thisID = t.ToString();
All this returns is:
System.Linq.Enumerable+WhereSelectArrayIterator`2[pu15.Models.C_ID,System.Int32]
But this never changes or shows what I want it to show.
What am I doing wrong?
You want to use Single or SingleOrDefault or First or FirstOrDefault, depending on what you want exactly, e.g.:
ViewBag.thisID = t.Single();
Single will throw an exception if there is more than one result or if there are no results.
If you'll use SingleOrDefault instead it will return null in case of more than one result or no result.
First on the other hand will fail only for an empty collection - when there is more than one element it will return the first one.
The most tolerant, FirstOrDefault will return null for empty result list, and the first element if there is more than one.
You have to remember that a where clause returns an IEnumerable<T> and not just one value. You have to take the first or a specific element of that collection and show it or if it is a collection of objects, show a property from that element.
In your case this would be:
var IDtext = IDMethod(MembID);
var allIDs = from ctext in cIDtext
where ctext.cid.ToString() == MembID
select cxt.C_ID;
var single = allIDs.SingleOrDefault();
//or:
var first = allIDs.FirstOrDefault();
ViewBag.thisID = single.ToString();
//or
ViewBag.thisID = first.ToString();
you can use method syntax, skip the Where clause, and just use First()
ViewBag.thisID = cIDtext
.First(c => c.cid.ToString() == MembID)
.C_ID.ToString();
t is an IEnumerable that may return many items. If you only want to use one of these items, you need to call t.First() or t.Single() ,eg:
ViewBag.thisID = t.First().ToString();
The difference between the two methods is that Single will throw an exception if there are more than 1 items in the results, while First will only throw if there are no matching results.
If you want to check that a result actually exists, you can use FirstOrDefault or SingleOrDefault, eg:
var id=t.FirstOrDefault();
if (id!=null)
ViewBag.thisID = id.ToString();
You can condense the call if you pass the condition as the predicate to First, FirstDefault etc:
var id=cIDtext.FirstOrDefault(ctext=> ctext.cid.ToString() == MembID);
The issue is that t is an IEnumerable and ToString will just give you the name of the type. You need to turn your enumeration into something like a comma separated list
ViewBag.thisID = string.Join(",", t.Select(v => v.ToString()));
Or more likely you only want one value and you need to use either First or Single.
ViewBag.thisID = t.First().ToString();
or
ViewBag.thisID = t.Single().ToString();
Use First if there are multiple return values and you only want the first, or you can use Last if you wan the last one. If the query should return only one value then you should use Single. There are also FirstOrDefault, LastOrDefault, and SingleOrDefault that will return a default value if the enumeration is empty or in the case of Single if the enumeration has more than one value. Single, First, and Last will throw an exception if the enumeration is empty or in the case of Single if the enumeration has more than one value. Just choose the appropriate one for your use case.

Calling FirstOrDefault multiple times in LINQ to set object properties

I have a LINQ query like so:
var Item = (from s in contextD.Items where s.Id == Id select s).ToList();
Further down the Item object properties are set like so:
Item.FirstOrDefault().Qty = qtyToUpdate;
Item.FirstOrDefault().TotalPrice = UItem.FirstOrDefault().Qty * UItem.FirstOrDefault().Price;
...
My question is, will calling FirstOrDefault always loop through the result set returned by the query?
Shouldn't a single call be made and put in an object like so:
MyObject objMyObject = new MyObject;
objMyObject = Item.FirstOrDefault();
and then go about setting objMyObject properties.
The first part using FirstOrDefault is actually in the production, I am trying to find out if that's the right way.
Regards.
will calling FirstOrDefault always loop through the result set
returned by the query?
FirstOrDefault() never loops through all result set - it either returns first item, or default if set is empty. Also in this particular case even enumerator will not be created - thus you are calling FirstOrDefault() on variable of List<T> type, simply item at index 0 will be returned (if list is not empty). If you will investigate Enumerable.FirstOrDefault() implementation:
IList<TSource> list = source as IList<TSource>;
if (list != null)
{
if (list.Count > 0)
{
return list[0];
}
}
But this will be invoked each time you are calling FirstOrDefault().
Also you are missing 'default' case. If you are chaining methods like in your first sample, you can get NullReferenceException if list is empty. So, make sure something was returned by query:
var item = Item.FirstOrDefault();
if (item == null)
return; // or throw
item.Qty = qtyToUpdate;
var uitem = UItem.FirstOrDefault();
if (uitem == null)
return; // or throw
item.TotalPrice = uitem.Qty * uitem.Price;
One more note - you have little difference in performance if you are performing FirstOrDefault() on in-memory collection. But difference will be huge if you will perform it without saving query results into list. In that case each FirstOrDefault() call will cause new database query.

IEnumerable<T>.Union(IEnumerable<T>) overwrites contents instead of unioning

I've got a collection of items (ADO.NET Entity Framework), and need to return a subset as search results based on a couple different criteria. Unfortunately, the criteria overlap in such a way that I can't just take the collection Where the criteria are met (or drop Where the criteria are not met), since this would leave out or duplicate valid items that should be returned.
I decided I would do each check individually, and combine the results. I considered using AddRange, but that would result in duplicates in the results list (and my understanding is it would enumerate the collection every time - am I correct/mistaken here?). I realized Union does not insert duplicates, and defers enumeration until necessary (again, is this understanding correct?).
The search is written as follows:
IEnumerable<MyClass> Results = Enumerable.Empty<MyClass>();
IEnumerable<MyClass> Potential = db.MyClasses.Where(x => x.Y); //Precondition
int parsed_key;
//For each searchable value
foreach(var selected in SelectedValues1)
{
IEnumerable<MyClass> matched = Potential.Where(x => x.Value1 == selected);
Results = Results.Union(matched); //This is where the problem is
}
//Ellipsed....
foreach(var selected in SelectedValuesN) //Happens to be integer
{
if(!int.TryParse(selected, out parsed_id))
continue;
IEnumerable<MyClass> matched = Potential.Where(x => x.ValueN == parsed_id);
Results = Results.Union(matched); //This is where the problem is
}
It seems, however, that Results = Results.Union(matched) is working more like Results = matched. I've stepped through with some test data and a test search. The search asks for results where the first field is -1, 0, 1, or 3. This should return 4 results (two 0s, a 1 and a 3). The first iteration of the loops works as expected, with Results still being empty. The second iteration also works as expected, with Results containing two items. After the third iteration, however, Results contains only one item.
Have I just misunderstood how .Union works, or is there something else going on here?
Because of deferred execution, by the time you eventually consume Results, it is the union of many Where queries all of which are based on the last value of selected.
So you have
Results = Potential.Where(selected)
.Union(Potential.Where(selected))
.Union(potential.Where(selected))...
and all the selected values are the same.
You need to create a var currentSelected = selected inside your loop and pass that to the query. That way each value of selected will be captured individually and you won't have this problem.
You can do this much more simply:
Reuslts = SelectedValues.SelectMany(s => Potential.Where(x => x.Value == s));
(this may return duplicates)
Or
Results = Potential.Where(x => SelectedValues.Contains(x.Value));
As pointed out by others, your LINQ expression is a closure. This means your variable selected is captured by the LINQ expression in each iteration of your foreach-loop. The same variable is used in each iteration of the foreach, so it will end up having whatever the last value was. To get around this, you will need to declare a local variable within the foreach-loop, like so:
//For each searchable value
foreach(var selected in SelectedValues1)
{
var localSelected = selected;
Results = Results.Union(Potential.Where(x => x.Value1 == localSelected));
}
It is much shorter to just use .Contains():
Results = Results.Union(Potential.Where(x => SelectedValues1.Contains(x.Value1)));
Since you need to query multiple SelectedValues collections, you could put them all inside their own collection and iterate over that as well, although you'd need some way of matching the correct field/property on your objects.
You could possibly do this by storing your lists of selected values in a Dictionary with the name of the field/property as the key. You would use Reflection to look up the correct field and perform your check. You could then shorten the code to the following:
// Store each of your searchable lists here
Dictionary<string, IEnumerable<MyClass>> DictionaryOfSelectedValues = ...;
Type t = typeof(MyType);
// For each list of searchable values
foreach(var selectedValues in DictionaryOfSelectedValues) // Returns KeyValuePair<TKey, TValue>
{
// Try to get a property for this key
PropertyInfo prop = t.GetProperty(selectedValues.Key);
IEnumerable<MyClass> localSelected = selectedValues.Value;
if( prop != null )
{
Results = Results.Union(Potential.Where(x =>
localSelected.Contains(prop.GetValue(x, null))));
}
else // If it's not a property, check if the entry is for a field
{
FieldInfo field = t.GetField(selectedValues.Key);
if( field != null )
{
Results = Results.Union(Potential.Where(x =>
localSelected.Contains(field.GetValue(x, null))));
}
}
}
No, your use of union is absoloutely correct.
The only thing to keep in mind is it excludes duplicates as based on the equality operator. Do you have sample data?
Okay, I think you are are haveing a problem because Union uses deferred execution.
What happens if you do,
var unionResults = Results.Union(matched).ToList();
Results = unionResults;

Categories