Difference between Where and Single - linq

I'm trying to figure out the difference between Where(Expression) and Single(Expression).
Is the Expression passed into single forwarded to a Where function?
eg, are these two statements the same?
var result = context.Persons.Single(p => p.ID == 5);
var result2 = context.Persons.Where(p => p.ID == 5).Single();

Single returns you a Person, whereas the Where will return you an IEnumerable<Person>.
Passing the where expression into the single is just syntactic sugar.
Both the lines are functionally equivalent. The first I imagine could be ever so marginally more efficient. It's also easier on the eye in my opinion.

There is another difference though. Single() will throw an exception if the predicate used returns more than one element. In case the filter, when applied on an enumerable returns only one element, it returns that element without throwing an exception.
int[] a = {1, 2, 3};
var odd_Nos = a.Single(num => num % 2 != 0) // will throw exception (an InvalidOperationException)
var even_Nos = a.Single(num => num % 2 == 0) // will not throw exception
If you want just the first to appear, you can use the First() or FirstOrDefault() method.

Related

LINQ to Entities does not recognize the method Double Round(Double, Int32, System.MidpointRounding) method

I've tried the below LINQ Query in Linqer it is working fine but it is giving the below error while i tried with C#
from IHeal_Mnt_Tickets in iHealEntities.iHeal_Mnt_Tickets
where
Tickets.Active == 1 &&
Tickets.MntID == 1 &&
Tickets.InsertedOn >= fromdate &&
Mnt_Tickets.InsertedOn <= todate &&
(new string[] { "Resolved", "Assigned" }).Contains(Tickets.status)
group Tickets by new {
Tickets.Instance
} into g
select new {
Instance = g.Key.Summus_Instance,
Assigned = (Int64?)g.Count(p => p.iHealID != null),
resolved = (System.Int64?)g.Sum(p => (p.status == "Resolved" ? 1 : 0)),
domain = (System.Int64?)g.Sum(p => (p.status == "Assigned" ? 1 : 0)),
iHeal_Closure = (Decimal?)Math.Round((Double)(Double)g.Sum(p => (p.iHeal_Cur_status == "Resolved" ? 1 : 0)) * 1.0 / (Double)g.Count(p => p.iHealID != null) * 100, 2, MidpointRounding.AwayFromZero)
};
The error is
"LINQ to Entities does not recognize the method 'Double Round(Double, Int32, System.MidpointRounding)' method, and this method cannot be translated into a store expression."
Not everything that's supported in the BCL has a direct equivalent in SQL. Given that this is the final part of the query, the simplest approach would be to just write a query which fetched all the data you need without rounding etc, and then transform that data into your preferred format using a local query:
var dbQuery = from item in source
where filter
select projection;
// The AsEnumerable() part is key here
var localQuery = from item in dbQuery.AsEnumerable()
select complicatedTransformation;
Using AsEnumerable() effectively just changes the compile-time type... so that the Select call is Enumerable.Select using a delegate rather than Queryable.Select using an expression tree.
I would hope that you could make the final transformation much simpler than your current approach though - things like (Double)(Double) really aren't necessary... and any time you convert from double to decimal or vice versa, you should question whether that's necessary or desirable... it's generally better to stick to either binary floating point or decimal floating point, rather than mixing them.

How to request with random row linq

I am slow today
There is a request
"Take random child and put it into another garden."
I changed the code, but error in the last line of code "Does not contain a definition…and no extension method":
var query = db.Child.Where(x => x.Garden != null);
int count = query.Count();
int index = new Random().Next(count);
var ch = db.Child.OrderBy(x => query.Skip(index).FirstOrDefault());
ch.Garden_Id = "1";
What am I doing wrong?
It's hard to tell what you're doing wrong, because you didn't say why the results you're getting does not satisfy you.
But I can see two possible mistakes.
You're counting items with x.Garden != null condition, but taking from all children.
Take returns IEnumerable<T> even when you specify it to return only 1 item, you should probably use First instead.
I think your k should be
var k = db.Child.Where(x => x.Garden != null).Skip(rnd.Next(0,q)).First();

Truncating a collection using Linq query

I want to extract part of a collection to another collection.
I can easily do the same using a for loop, but my linq query is not working for the same.
I am a neophyte in Linq, so please help me correcting the query (if possible with explanation / beginners tutorial link)
Legacy way of doing :
Collection<string> testColl1 = new Collection<string> {"t1", "t2", "t3", "t4"};
Collection<string> testColl2 = new Collection<string>();
for (int i = 0; i < newLength; i++)
{
testColl2.Add(testColl1[i]);
}
Where testColl1 is the source & testColl2 is the desired truncated collection of count = newLength.
I have used the following linq queries, but none of them are working ...
var result = from t in testColl1 where t.Count() <= newLength select t;
var res = testColl1.Where(t => t.Count() <= newLength);
Use Enumerable.Take:
var testColl2 = testColl1.Take(newLength).ToList();
Note that there's a semantic difference between your for loop and the version using Take. The for loop will throw with IndexOutOfRangeException exception if there are less than newLength items in testColl1, whereas the Take version will silently ignore this fact and just return as many items up to newLength items.
The correct way is by using Take:
var result = testColl1.Take(newLength);
An equivalent way using Where is:
var result = testColl1.Where((i, item) => i < newLength);
These expressions will produce an IEnumerable, so you might also want to attach a .ToList() or .ToArray() at the end.
Both ways return one less item than your original implementation does because it is more natural (e.g. if newLength == 0 no items should be returned).
You could convert to for loop to something like this:
testColl1.Take(newLength)
Use Take:
var result = testColl1.Take(newLength);
This extension method returns the first N elements from the collection where N is the parameter you pass, in this case newLength.

linq iteration need for single row?

when i form the following code
Rabbit[] rbt=
new Rabbit[]
{
new Rabbit{ Name="Jobby", Vegetable=new Vegetable{ VegiName="carrot"}},
new Rabbit{ Name="hobby", Vegetable=new Vegetable{ VegiName="Beetroot"}}
};
var s = from bt in rbt where
bt.Vegetable.VegiName.CompareTo("carrot") == 0 select bt;
foreach (var v in s)
{
Console.WriteLine("{0}{1}",v.Vegetable.VegiName,v.Name);
}
my query returns single row only, then why do i need foreach iteration ? can't i use
Console.WriteLine("{0}{1}",s.Vegetable.VegiName,s.Name); directly ?
How is the compiler meant to know that your query will only return a single row?
Suppose you changed your data to include another carrot - or no carrot at all - what would you expect to happen.
If you should definitely get a single result, call Single to get it. If you may get multiple results, call First. If you may get multiple results or none, call FirstOrDefault. If you may get zero or one result, call SingleOrDefault. For example, for the first case:
var s = from bt in rbt where
bt.Vegetable.VegiName.CompareTo("carrot") == 0 select bt;
var veg = s.Single();
Console.WriteLine("{0}{1}",veg.Vegetable.VegiName,veg.Name);
Or alternatively:
var veg = rbt.Where(bt => bt.Vegetable.VegiName.CompareTo("carrot") == 0)
.Single();
Console.WriteLine("{0}{1}",veg.Vegetable.VegiName,veg.Name);
Because the type of the query IEnumerable<Rabbit>, not Rabbit. You can get what you want, though, by using a FirstOrDefault() or SingleOrDefault() at the end of your query.
var s = (from bt in rbt
where bt.Vegetable.VegiName.CompareTo("carrot") == 0
select bt)
.FirstOrDefault();
No, because LINQ does not know at compile time that s will return only a single row. Use the First() or Single() operator:
var v = (from bt in rbt where
bt.Vegetable.VegiName.CompareTo("carrot") == 0
select bt)
.First();
You can use the Single method to return a single value. Use SingleOrDefault if you suspect it may be null (ie. no results returned).
Rabbit result = s.SingleOrDefault();
if (result != null)
{
// use result
Console.WriteLine("{0} : {1}", result.Vegetable.VegiName, result.Name);
}
As others have mentioned, the alternative is to use First or FirstOrDefault to take the first result returned, if the query returns numerous results.

Reproduce a "DELETE NOT IN" SQL Statement via LINQ/Subsonic

I want to do something like DELETE FROM TABLE WHERE ID NOT IN (1,2,3) AND PAGEID = 9
I have a List of IDS but that could be changed if needs be. I can't work out how to get a boolean result for the LINQ parser.
Here is what Subsonic expects I think.
db.Delete(content => content.PageID == ID).Execute();
I can't work out how to do the NOT IN statement. I've tried the List.Contains method but something not quite right.
UPDATE: One alternative is to do:
var items = TABLE.Find(x => x.PageID == ID)'
foreach(var item in items)
{
item.Delete();
}
This hits the database a lot more though
When you say "something not quite right" what exactly do you mean?
I'd expect to write:
List<int> excluded = new List<int> { 1, 2, 3 };
db.Delete(content => !excluded.Contains(content.PageID)).Execute();
Note that you need to call Contains on the array of excluded values, not on your candidate. In other words, instead of saying "item not in collection" you're saying "collection doesn't contain item."
Try .Contains:
db.Delete(content => content.PageID.Contains(<Array containing ID's>).Execute();
(the above is just an example, might need some polishing for your specific situation)
I have found that this works but its not via LINQ
var table = new WebPageContentTable(_db.DataProvider);
var g = new SubSonic.Query.Delete<WebPageContent(_db.DataProvider)
.From(table)
.Where(table.ID)
.NotIn(usedID)
.Execute();
I have found that this does work and via LINQ - however it hits the database multiple times.
var f = WebPageContent.Find(x => !usedID.Any(e => e == x.ID));
if (f.Count > 0)
{
var repo = WebPageContent.GetRepo();
repo.Delete(f);
}
This I imagine would work in one hit to the database but I get an exception thrown in QueryVisitor::VisitUnary
WebPageContent.Delete(x => !usedID.Any(e => e == x.ID));

Resources