Linq Lambda Where clause with in a where clause - c#

I am trying to build a lambda expression with serval where clauses with in each other.
Items, webProperties and profiles are all lists. I am trying to find the one that contains a profile which is 3 level lists down. Actually all I am really trying to do is validate that it does exist.
var x = AccountSummeriesResponse.items.Where(wp => wp.webProperties.Where(p => p.profiles.Where(a => a.id == profile ))).FirstOrDefault();
I am getting the following error.
Cannot implicitly convert type
'System.Collections.Generic.IEnumerable'
to 'bool'

The probelm is Enumerable.Where returns IEnumarable<T> but the predicate of Where expects a boolen. You can use Any instead:-
var x = AccountSummeriesResponse.items
.Where(wp => wp.webProperties.Any(p => p.profiles.Any(a => a.id == profile )))
.FirstOrDefault();
Also, you can replace the Where with FirstOrDefault like this:-
var x = AccountSummeriesResponse.items
.FirstOrDefault(wp => wp.webProperties.Any(p => p.profiles
.Any(a => a.id == profile )));

That's because delegate(Predicate) inside the where clause needs to return bool and you are returning IEnumerable(Where(p => p.profiles.Where(a => a.id == profile ))) so reporting a compilation error.
Instead make a use of Any extension method if you are looking for whether exist in the collection kind of thing..
var x = AccountSummeriesResponse.items.Where(wp => wp.webProperties.Any(p => p.profiles.Any(a => a.id == profile ))).FirstOrDefault();

Related

linq - find item in list within multiple lists

I have a highly nested class, and trying to find a single item buried deep within. The following gives me an error "Can't convert type match to bool', although I don't see why it thinks I'm trying to return a boolean.
var match = community.TeamLeagues
.Where(x => x.Seasons
.Where(y => y.Divisions
.Where(z => z.Matches
.Where(a => a.Id == "1234").FirstOrDefault())));
Where by itself returns a (deferred) enumerable of items and cannot as such be used as a condition by the outer Where. What you probably want to do is to use Contains(), Any() or All() inside the outer Wheres that will return the result you're looking for.
Something like this might be what you're after:
var match = community.TeamLeagues.Where(t =>
t.Seasons.Any(
s => s.Divisions.Any(
d => d.Matches.Any(
m => m.Id == "1234")
)));
The Where method needs to evaluate an expression that returns a bool. Your nested Wheres are not doing that - the only Where that is, is the last one a => a.Id == "1234", all the other expressions are returning an IEnumerable.
z.Matches.Where(a => a.Id == "1234").FirstOrDefault() returns a object of type Match(your collection item type of the IEnumerable Matches) (or null), no boolean value. I guess you need to check if there are entires in matches that have a Id 1234. Use Any to evaluate a condition:
var match = community.TeamLeagues.Where(x =>
x.Seasons.Any(y =>
y.Divisions.Any(z =>
z.Matches.Any(a => a.Id == "1234")
)));
[items.Where(x => x.Id == 4).Any() is the same as items.Any(x => x.Id == 4)]
This returns you all TeamLeagues which contain a Season which contain a Division which contain a Match which has a element with the id 1234.
To make it simple you can also use the Matches table directly and using a ViewModel you can represent your view.
like:
var MyViewModel = (from l in Mathes
where l.Id == "1234"
select new MyViewModel
{
Id = l.Id,
MatchName = l.Name,
}).ToList();
Couldn't get it working with linq, but works with query syntax.
var leagueMatch = (from teamLeague in community.TeamLeagues
from season in teamLeague.Seasons
from division in season.Divisions
from match in division.Matches.Where(x => x.Id == "1234")
select match).FirstOrDefault();

Build dynamic Lambda Expression for comparing undefined number of values

In short what I want do accomplish is to load Tasks from a project in SharePoint Project Server using CSOM.
var projects = ctx.LoadQuery(ctx.Projects
.Where(p => p.Id == projGuid)
.Include(
p => p.Id, p => p.Name,
p => p.Tasks
.Where(t => t.Id == taskGuid)
.Include(t => t.Name))
);
ctx.ExecuteQuery();
My Problem is with this part .Where(t => t.Id == taskGuid). It works how it should if I only want to load 1 Task but would not work if I want to load more then one. Sure I could write it like that .Where(t => t.Id == taskGuid1 || t.Id == taskGuid2 || ... )
But that wouldn't be dynamic.
What I tried was to use an array and the look if the array GuidArray.Contains(p.Id)
But I get an error if I try to use .Contains() inside the Where() expression.
ClientRequestException: The 'Contains' member cannot be used in the expression.
So I was thinking if it is possible to somehow create the lambda expression based on the number of tasks I want to load.
I regards to creation of lambda, you create the dynamic or condition you are looking for like so
public static class ExpressionExt
{
public static IQueryable<T> Where<T,TKey>(this IQueryable<T> data, string prop,params TKey[] guids)
{
var param = Expression.Parameter(typeof(T));
var exp = guids.Select(g => Expression.Equal(Expression.Property(param, prop), Expression.Constant(g))).Aggregate((a, e) => a != null ? Expression.Or(e, a) : e);
var lambda = Expression.Lambda<Func<T, bool>>(exp, param);
return data.Where(lambda);
}
}
And use it like Where(nameof(A.Id), guids) this is what I usually do when the IQueryable only supports or and not contains. There might a contains implementation there so you might want to check the documentation.

how to use linq to return a value from 4 tables

I am reasonably new to Linq and it seems quite easy to iuse but I am having an issue when trying to extract a value from a table that is linked/constrained by 3 other tables.
I have this in my SQL DB:
I am using Asp.Net 4 and Entity Framework 6.
I have as a parameter the 'DatabaseName'.
I ultimately want to get the SubscriptionRef that is assigned to this name.
I could do this step-by-step (ie using multiple linqs) but I thought it would look 'clean' using just 1 linq statment.
I have got as far as this:
var names = o.RegisteredNames.Where(d => d.DatabaseName == DBName).Where(d => d.ClientNames.Where(f => f.ClientId == f.Client.ClientId).FirstOrDefault();
But I get the error:
Cannot implicitly convert type 'Services.ClientName' to 'bool'
You have a Problem here:
d => d.ClientNames.Where(f => f.ClientId == f.Client.ClientId)
f => ... returns a single ClientName or null, which causes your error, because there should be a boolean.
If you want this first value or null, you should replace
.Where(d => d.ClientNames ...
//with
.Select(d => d.ClientNames ...
Try this:
o.RegisteredNames.First(d => d.DatabaseName == DBName).ClientNames.Select(x=>x.Client.Subscription.SubscriptionRef)
It should give you list go SubscriptionRef.
You can try with one LINQ query like...
var names = o.RegisteredNames.Where(d => d.DatabaseName == DBName ).FirstOrDefault();
You might wanna try sql style:
var client = from c in db.Clients
join cn in db.ClientNames on c.ClientId equals cn.ClientId
join rn in db.RegisteredNames on cn.RegisteredNamesId equals rn.RegisteredNameId
where rn.DatabaseName == "YourDBName"
select c;
But it also depends on how your objects were built.
Try using join:
var names = (
from names in o.RegisteredNames.Where(d => d.DatabaseName == DBName)
join cnames in o.ClientNames on names.RegisteredNamesId equals cnames.RegisteredNamesId
select cnames.ClientId
).FirstOrDefault();
Add as many joins as you want.
Try this
It works in List,
var option1= o.RegisteredNames
.Where(g => g.DbName == "YourDbName")
.Where(h => h.ClientNames.Any(f => f == 5))
.FirstOrDefault();
var option2= o.RegisteredNames
.FirstOrDefault(h => h.DbName == "Name" && h.ClientNames.Any(j => j == 1));

Convert this LINQ to Lambda Expression?

I have a LINQ, it works fine. My question is: how to convert it to Lambda Expression?
var searchResults = from study in dataContext.Studies
join location in dataContext.Locations
on study.LocationID equals location.LocationID
join doctorLocation in dataContext.DoctorLocations
on location.LocationID equals doctorLocation.LocationID
join doctor in dataContext.Doctors
on doctorLocation.DoctorID equals doctor.DoctorID
where doctor.DoctorID == doctorId
select study;
I think LINQ is more natural to me (similar to SQL script). However, in this case, I just want to convert it to Lambda Expression, but I could not make it work.
I got stuck at:
var searchResults = dataContext.Studies.Where(x =>
x.Location.DoctorLocations.FirstOrDefault() != null &&
x.Location.DoctorLocations.FirstOrDefault().DoctorID == doctorId);
This only works for FirstOrDefault. Since there are multiple DoctorLocations, I do not how to write this one.
Try this:
var searchResults = dataContext.Studies.Where(x =>
x.Location != null
&& x.Location.DoctorLocations.Any(dl => dl.DoctorID == doctorId));
you will get all Studies related to at least one DoctorLocation with DoctorID equals doctorId
var searchResults = dataContext.Studies
.Include(x => x.Locations)
.Include(x => x.DoctorLocations)
.Include(x => x.Doctors)
.Where(x => x.[InheritedPathToDoctor].DoctorId == id)
.Select(x => x.[InheritedPathToStudy].Study)
.FirstOrDefault() OR .ToList()
I have made a lot of assumptions here as to how you have set up your context. I've assumed it's a relational database and therefore the includes simply means it returns all data. I haven't tested it though so there are probably a few errors.
You require an include for every class and the where is pretty self explanatory.

How to convert this linq query to lambda?

I have this Query :
return (from r in this.Requests where r.Status == "Authorised" from i in r.Items select i).Sum(i => i.Value);
I tried converting it to lambda as I prefer that now, so I did :
var sum = Requests.Where(x=>x.Status == "Authorised").Select(x=>x.Items).Sum(x=>x.Value); --> and here I got no Value Item, any ideas why?
You need SelectMany instead of Select. Your query is basically equivalent to:
return this.Requests
.Where(r => r.Status == "Authorised")
.SelectMany(r => r.Items)
.Sum(i => i.Value);
Note that your original query would have been clearer on multiple lines too...

Categories