Convert an ordinary LINQ query to lambda expression - linq

from _tupleRows in this.TupleSet
from _member in _tupleRows.Members
where (_member.HasChildMembers && !_member.DrilledDown)
select new
{
_member1 = _member,
_member2 = (from _searched in this.TupleSet
from _compareMember in _searched.Members
where (_member.UniqueName == _compareMember.UniqueName &&
_member.LevelDepth == _compareMember.LevelDepth &&
_compareMember.DrilledDown)
select _compareMember).FirstOrDefault()
};
I need to convert this simple LINQ expression to an equivalent lambda expression

from _tupleRows in this.TupleSet
from _member in _tupleRows.Members
where (_member.HasChildMembers && !_member.DrilledDown)
select new
{
_member1 = _member,
_member2 = (from _searched in this.TupleSet
from _compareMember in _searched.Members
where (_member.UniqueName == _compareMember.UniqueName
&& _member.LevelDepth == _compareMember.LevelDepth
&& _compareMember.DrilledDown)
select _compareMember).FirstOrDefault()
};
becomes:
this.TupleSet
.SelectMany(tupleRows =>
tupleRows.Members
.Where(member => member.HasChildMembers && !member.DrilledDown)
.Select(member => new
{
_member1 = member,
_member2 = this.TupleSet
.SelectMany(searched =>
searched.Members
.Where(compareMember =>
member.UniqueName == compareMember.UniqueName
&& member.LevelDepth == compareMember.LevelDepth
&& compareMember.DrilledDown))
.FirstOrDefault(),
}));

They're really not hard to convert. Just start at the top and append as you go.
Something like:
this.TupleSet.SelectMany(ts => ts.Members)
.Where(m => m.HasChildMembers && !m.DrilledDown)
.Select(m => new
{
_member1 = m,
_member2 = this.TupleSet.SelectMany(ts => ts.Members)
.Where(other => m.UniqueName == other.UniqueName
&& m.LevelDepth == other.LevelDepth
&& other.DrilledDown
)
.FirstOrDefault()
});
No guarantees this is perfect or will even compile, but then I'm not trying to do your work for you, I'm trying to give you a demonstration of how you might do these conversions yourself.
Also, do yourself a favor and use simpler identifiers in lambdas when you can. They're very limited scope, and you're chaining calls to make one big statement, so often it will usually be very obvious in context what a variable means. I may not have the best example here, but the original variable names were way too verbose, IMO.
The nested query also seems a little strange. If there is a way to structure your data so you don't have to do that nested query I think you'll get better perf and simpler queries.

Related

Using StringComparison.OrdinalIgnoreCase to ignore Case

This code is case sensitive, how to make it case insensitive?
return HeaderNames.Length == fileLine.Count &&
HeaderNames
.Select(headItem => fileLine[Array.IndexOf(HeaderNames, headItem)] == headItem)
.All(i => i);
Thanks for your answers/
How about using FindIndex instead of IndexOf.
I assume you have array of strings
Array.FindIndex(HeaderNames, t => t.IndexOf(headItem, StringComparison.InvariantCultureIgnoreCase) >=0);
Example:
var a = new string[]{"green","red"};
var idx = Array.FindIndex(a, t => t.IndexOf("Red", StringComparison.InvariantCultureIgnoreCase) >=0);
Console.WriteLine(idx);
Output
1
Note: You could also use string.Equals instead of t.IndexOf in above suggestion
t=>string.Equals(t, headItem, StringComparison.InvariantCultureIgnoreCase)
It will be a long nested LINQ though.
Edit
If you want to find exact string i.e. dont want to get index of «ed» from «Red» as item existing in array. Use string.Equals.
There is no reason to index back into the array to get the position, there is a version of Select for that. You can use String.Equals and pass the StringComparison option:
return HeaderNames.Length == fileLine.Count &&
HeaderNames.Select((headItem, i) => fileLine[i].Equals(headItem, StringComparison.OrdinalIgnoreCase)).All(i => i);

Chaining together IQueryable in an OR fashion

I've got some IQueryables I would like to OR together. My understanding is chaining them together using Where results in ANDing them together (though my understanding may be faulty.)
Here's an example of a query I would like to OR together, in lieu of using all the chained Where clauses:
var query = query.Where(x => x.Value == 1)
.Where(x => x.Name == name);
Instead of that, I'm looking for something like:
var query = query.Where(x => x.Value == 1)
.Or(x => x.Name == name)
.Or(x => x.SomeOtherValue == something else)
.Or(x => x.AChildObject.Items.Where(item => item.SomeValue == yet something else));
...in my case there are probably 10 or items I'd need to chain together like above.
I've been looking over posts like this one and I suppose I could use a series of || statements to chain things together, but am not sure if that's the way to go. It could get very hard to read.
In researching this online I'm running into meaty articles on PredicateBuilders and the like. I'm not an expert on LINQ by any means and I was hoping for some guidance?
It looks like that you can just use OR operator.
var query = query.Where(x => x.Value == 1
|| x.Name == name)
|| x => x.SomeOtherValue == something else)
|| x => x.AChildObject.Items.Where(item => item.SomeValue == yet something else))
You can also use Union or Concat if the data being combined from different sources, see Linq union usage?

How do I add a condition to a LINQ query that uses groups?

Here is my current, working, query:
var lsFooterRow = from i in _context.Inventory
where i.ClaimId == claimID
&& i.Taxable == false
group i by new { i.ClaimId }
into grp
select new
{
SumOfReplValue = grp.Sum(i => i.Price),
SumOfACV = grp.Sum(i => i.ACV),
SumOfReplCost = grp.Sum(i => i.ReplacementCost)
};
What I'd like to add, to make it conditional, is something like this so that it adds a filter to the base query along with ClaimID and Taxable:
if (reportType == "R")
lsFooterRow = lsFooterRow.Where(i => i.ReplCost > 0);
This is not working because is does not recognize ReplCost, only SumOfReplValue, SumOfACV and SumOfReplCost.
Can someone please tell me, without doing the query in two steps, a way to add this condition? If there is no way to do it, a two step approach would be greatly appreciated :-)
Thanks in Advance!
If I understand correctly, you should probably break out intial query into multiple pieces.
var lsFooterRow = from i in _context.Inventory
where i.ClaimId == claimID
&& i.Taxable == false
select i;
// conditional where
if (reportType == "R")
lsFooterRow = lsFooterRow.Where(i => i.ReplacementCost > 0);
var aggregateFooterRow = from i in lsFooterRow
group i by new { i.ClaimId }
into grp
select new
{
SumOfReplValue = grp.Sum(i => i.Price),
SumOfACV = grp.Sum(i => i.ACV),
SumOfReplCost = grp.Sum(i => i.ReplacementCost)
};
That way you are filtering on the replacement cost before it gets aggregated, which sounds like what you want to do.
You did express concerns about a two part query but that shouldn't really pose a problem. The nice thing about this is it will not compose and execute the sql until you enumerate over the final version of the query. The entity framework engine is smart enough to simply add the second where as another condition in the final where statement in SQL. That means that your contitional where will neatly be part of the query rather than an afterthought. You can break up the query and add conditional things as much as you want.
The ability to break up the query into multiple pieces and conditionally compose the query is a tremendous benefit that LINQ has over SQL.
#Devin's answer is probably cleanest and makes an important pedant point about the delayed execution of linq and how 2 steps doesn't mean two queries. That being said, if you want to do it in one query, you could write the first query to include the extra condition like so:
var lsFooterRow = from i in _context.Inventory
where i.ClaimId == claimID
&& i.Taxable == false
&& (i.ReplacementCost > 0 || reportType != "R")
group i by new { i.ClaimId }
into grp
select new
{
SumOfReplValue = grp.Sum(i => i.Price),
SumOfACV = grp.Sum(i => i.ACV),
SumOfReplCost = grp.Sum(i => i.ReplacementCost)
};
EDIT:
Hmm, the only thing I can think of that would make this fail, and #Devin's work, is if you're changing the value of reportType between this declaration and where ever the actual enumeration of lsFooterRow is occurring. If this is happening, you can always just .ToList() it immediately. Or, less resource intensively, copy the reportType to a temporary variable that is never changed, and reference that within your query instead.
string _reportType = reportType //only set here, nowhere else
var lsFooterRow = from i in _context.Inventory
where i.ClaimId == claimID
&& i.Taxable == false
&& (i.ReplacementCost > 0 || _reportType != "R")
group i by new { i.ClaimId }
into grp
select new
{
SumOfReplValue = grp.Sum(i => i.Price),
SumOfACV = grp.Sum(i => i.ACV),
SumOfReplCost = grp.Sum(i => i.ReplacementCost)
};
But now it's no longer as clean, and captures the _reportType variable unnecessarily within the closure.
if you know what sql query needs to be executed at the DB...you can try http://www.linqpad.net/
which I feel is pretty handy when u work with linq...

How to convert a LINQ query from query syntax to query method

Linq and EF4.
I have this Linq query in query syntax I would like convert into query method.
Are you able to do it? I tried more tha 2 hours without success :-(
Thanks for your time
CmsContent myContentObj = (from cnt in context.CmsContents
from categoy in cnt.CmsCategories
where categoy.CategoryId == myCurrentCategoryId && cnt.ContentId == myCurrentContentId
select cnt).Single();
My original answer selected the wrong item. It's a bit more complicated than what I had (which Ani has posted). Here's what I believe is an equivalent query however and should perform better:
CmsContent myContentObj =
context.CmsContents
.Where(cnt => cnt.ContentId == myCurrentId
&& cnt.CmsCategories
.Any(categoy => categoy.CategoryId == myCurrentCategoryId))
.Single();
Here is a non-direct translation that I believe performs the same task in much less code:
var myContentObj = context.CmsContents.Single(
x => x.ContentId == myCurrentContentId &&
x.CmsCategories.Any(y => y.CategoryId == myCurrentCategoryId)
);
Here's how the C# compiler actually does it, with some help from .NET Reflector to verify:
var myContentObj = context
.CmsContents
.SelectMany(cnt => cnt.CmsCategories,
(cnt, categoy) => new { cnt, categoy })
.Where(a => a.categoy.CategoryId == myCurrentCategoryId
&& a.cnt.ContentId == myCurrentContentId)
.Select(a => a.cnt)
.Single();
Essentially, the 'nested' from clauses results in a SelectMany call with a transparent identifier (an anonymous-type instance holding the 'parent' cnt and the 'child' categoy). The Where filter is applied on the anonymous-type instance, and then we do another Select projection to get back the 'parent'. The Single call was always 'outside' the query expression of course, so it should be obvious how that fits in.
For more information, I suggest reading Jon Skeet's article How query expressions work.

If condition in LINQ Where clause

With Linq, can I use a conditional statement inside of a Where extension method?
var query = someList.Where(a => (someCondition)? a == "something" : true);
so, if 'someCondition' is false, 'Where' will be skipped.
Yes you can like:
var query = someList.Where(a => a == "something");
if (condition)
{
query = query.Where(b => b == "something else");
}
var result = query.ToList();
Because Where is producing an IQueryable, the execution is deferred until the ToList in my example so you can chain Wheres together as much as you want and then just execute it after you have passed all your conditions.
Make use of WhereIf extenstion method avaialbe in linq
Example
if (SearchControlMain.PostingID.HasValue)
query = query.Where(q => q.PostingID == SearchControlMain.PostingID);
instead of above go for the below
query = query.WhereIf(SearchControlMain.CategoryID.HasValue, q => q.CategoryID == SearchControlMain.CategoryID);
LINQ WhereIf Extension Method
LINQ to SQL Where Clause Optional Criteria
Not sure if this is appropriate but it is quite useful, you can use ifs quite handily with conditional where clauses:
var r = (from p in productinfo.tblproduct
where p.Accountid == accountid
select p);
if (uuf1 != null)
r = r.Where(p => p.UnitUserField1 == uuf1);
if (uuf2!= null)
r = r.Where(p => p.UnitUserField2 == uuf2);
So the where clause will be amended according to what is in UUF1 or UUF2 i.e. you might have only UUF1 with info, in which case it will take that and ignore the UUF2 where clause, you might have both in which it will take both or you might not have anything in UUF1 or 2 and your where clause will just take the accountid as the where clause.
In my case there were two "conditional" where depending on search keys, so I did:
var query = db.Package.Include("SomeThing")
.Where(item => searchString1 == null || searchString1 == "" || item.Contains(searchString1))
.Where(item => searchString2 == null || searchString2 == "" || item.Contains(searchString2));
...
from item in items
where condition1
&& (condition2 ? true : condition3)
select item
This is how can you can do it with the noob Linq syntax.
This applies the condition3 only if condition2 is false.
If condition2 is true, you are essentially doing && true which has no effect on the where clause.
So it is essentially doing this:
if(condition2)
{
from item in items
where condition1
select item
else
{
from item in items
where condition1
&& condition3
select item
}
I had a scenario like this where I had to check for null within the list itself. This is what I did.
items = from p in items
where p.property1 != null //Add other if conditions
select p;
// Use items the way you would use inside the if condition
But as Kelsey pointed out this would work too -
items = items.Where(a => a.property1 != null);
I'm not sure what the question is, but a possible answer could be:
Yes,
list.Where(item => { if (Foo(item)) return true; else return false; });
It would be a complicated way of saying something simple, though.
In my case, I wanted to keep the elements which met my criteria and log the ones that didn't without iterating multiple times.
var merchantsWithLocations = allMerchants.Where(m =>
{
if (m.Locations?.Any() != true)
{
_logger.Log("Merchant {merchantId} has no locations", m.Id);
return false;
}
return true;
};
Any time you want to do a side-effect per element (such as logging), breaking out the lambda into a statement body makes it easy to reason about.

Resources