Following is the case for which I intend to write my LINQ query, but it does not work:
List <Clients> has client info and in this list there is List<JobCards>, which represents the list of job cards of that person. The job card list has one attribute named Status. I need to return all the active job cards in all Clients List.
Can it be done in LINQ?
Other code may be as follows:
BindingList<JobCard> activeJobs = new List<JobCards>();
foreach(var client in allClientList)
{
foreach(var jobCard in client.JobCards)
{
if (jobCard.Status == "Active")
{
activeJobs.Add(jobCard);
}
}
}
I am in the learning phase of LINQ. I am certain there is some error in what I've implemented, but I'm not sure how to fix it. Here is my LINQ query:
activeJobCards = new BindingList<JobCard>(
mainForm.allData.Where(
index => index.JobCards.Where(
card => card.Status == "active"
).ToList().Count > 0
).ToList()
);
What you are looking for is SelectMany
allClientList.SelectMany(c => c.JobCards).Where(jc => jc.Status == "Active");
SelectMany() will give you an IEnumerable<JobCard> and from there you select the Active job cards.
allClientList.SelectMany(c => c.JobCards.Where(jc => jc.Status == "Active"));
BindingList<JobCard> activeJobs = new List<JobCards>();
activeJobs.AddRange(allClientList.SelectMany( x=> x.JobCards)
.Where( y => y.Status == "Active"));
Related
results = await query
.GroupJoin(_invitations.GetAll().AsNoTracking()
.Where(i => i.GroupId == groupId),
user => user.Id,
invitation => invitation.UserId,
(a, s) => new { User = a, Invitation = s})
.SelectMany(
ai => ai.Invitation.DefaultIfEmpty(),
(a, s) => new { Users = a.User, Invitations = s }
)
.Select(i => i.Users)
.Skip(skip)
.Take(take)
.ToListAsync();
Howdy. I have quite simple group join but I can't figure out how to sort invitations inside this group join. Problem is, I don't want to make simple orderBy(i => i.creationDate) but I want to do something like (but on queryable):
var list = new List<string> {"Fall","Mid","Spring"};
return _db.MiaLog1A.Where(m => m.Campus == selectedCampus)
.AsEnumerable()
.OrderBy(m => m.StudentName)
.ThenBy(m=> list.IndexOf(m.Term));
I need to order them by specific strings. Because I have a scenario where newest invitation isn't what I need. I want accepted invitation status first. Is there a way to do this?
I've tried things like that:
.OrderBy(i => sortOrderList.IndexOf(i.InvitationsStatus) in many places but it just throws.
Thank you in advance.
A pattern of
.OrderBy(m => m.StudentName)
.ThenBy(m => m.X == "y" ? 0 : (m.X == "x" ? 1:2))
Should translate if you want to sort by "y","x","z"
But I note that both examples you given; (fall, mid, spring) and (approved vs pending / not invited) are sorted in that order anyway.. you don't need to sort by the list index of these, you can just sort the strings ascending
For the part of database below I need find what team in season scored most goals per one match.
For that I used methods
public List<Tour> GetAllTours(Guid seasonGuid){...}
and
public List<SimpleMatch> GetMatches(Guid tour)
{
using (var db = new ConnectToDb())
{
if (!db.Matches.Any()) return new List<Match>();
var matches = db.Matches;
var matchesToReturn = new List<Match>();
foreach (var item in
matches
.Include(x => x.Home)
.Include(x => x.Guest)
.Include(x => x.Result)
.Include(x => x.Tour))
{
if (item.Tour.Id != tour)
matchesToReturn.Add(item);
}
return matchesToReturn;
}
}
and
public List<SimpleTeam> GetTeamMostGoalInSeason(List<Match> matches){...}
where SimpleTeam is a team with count goals, if teams with max count == many, used List
it's method not tiny, and I don't know how do this with LINQ query.
Not very efficient, but my idea would be something like this:
matches.SelectMany(match => new[] {
new TeamScore {
TeamId = match.HomeId,
Goals = match.Result.HomeTeamGoals
},
new TeamScore {
TeamId = match.GuestId,
Goals = match.Result.GuestTeamGoals
}
})
.GroupBy(score => score.Goals)
.OrderByDescending(group => group.Key)
.First()
.Select(score => score.TeamId);
where TeamScore is a simple struct.
This will return an IEnumerable with all the teams that scored the most goals (if it was the same number). You will probably have to change some of the property names or other details. I'm not familiar with LINQ to databases.
If you only want one, you could do First() but that would pick the first one and ignore others depending on the order. SingleOrDefault() would return null when it's a draw, if that's what you want.
EDIT: To get all matches in all tours, you would do something like this:
GetAllTours(...).SelectMany(tour => GetMatches(tour))
but since you're dealing with a database, you could just ask for all matches directly.
I have entities that are nested in this order:
RootDomain
Company
CompaniesHouseRecord
CompanyOfficer
When given a RootDomain I want to create a list of all CompanyOfficers that have an email address but I am not sure how to do this.
Here Is my non-working attempt:
RootDomain rd = db.RootDomains.Find(123);
List<CompanyOfficer> col = rd.Companies.Where(x => x.CompaniesHouseRecords.Any(chr => chr.CompanyOfficers.Any(co => co.Email != null)))
.Select(x => x.CompaniesHouseRecords.Select(chr => chr.CompanyOfficers)).ToList();
I am obviously way off the mark here. Can someone show me or point me to the correct method for dong this?
Like this:
RootDomain rd = db.RootDomains.Find(123);
List<CompanyOfficer> col = rd.Companies
.SelectMany(c => c.CompaniesHouseRecords)
.SelectMany(c => c.CompanyOfficers)
.Where(o => null != o.Email).ToList();
Someone answered before me, but I can show something different, which can be more convenient for someone who is used to DB requests.
Using LINQ, you can do this type of request:
var officersWithEmail = from company in rd.Companies
from companiesHouseRecord in company.CompaniesHouseRecords
from companyOfficer in companiesHouseRecord.CompanyOfficers
where (companyOfficer.Email != null)
select companyOfficer;
Some people will find it more readable.
If you want to obtain a List<> as output, just use .ToList on the query.
So I'm trying to run a Linq query that's analogous to the SQL query:
SELECT COUNT(*)
FROM table
WHERE ww >= [wwStartSelected]
AND ww <= [wwEndSelected]
AND manager='[managerName]'
AND status='Done'
GROUP BY ww;
To get a count of the number of rows (tasks) within the given ww range that are marked as done under a particular manager and grouped by ww. I've tried to create a LINQ query that would return something similar (wwStartSelected && wwEndSelected are global vars):
protected List<int> getManagerDoneCount(string managerName)
{
using (var context = new InfoDBContext())
{
List<int> managerDoneCount = context.InfoSet.Where(x => x.ww >= wwStartSelected && x.ww <= wwEndSelected && x.manager == managerName && x.status == "Done").GroupBy(x => x.ww).Count().ToList();
return managerDoneCount;
}
}
This query would then feed into a chart:
var testChart = new Chart(width: 600, height: 400)
.AddTitle("Test")
.AddSeries(
name: "Done",
chartType: "StackedColumn100",
xValue: new[] { WWList },
yValues: new[] { getManagerDoneCount("someManager") })
However I'm running into an issue with my Linq line and it says:
'int' does not contain a definition for 'ToList' and no extension method 'ToList'
accepting a first argument of type 'int' could be found (are you
missing a using directive or an assembly reference?)
Is there way to get fix this easily or do I have to convert to string, then convert back to int for the chart series (the latter of which seems a bit silly [but if so, best way to do so])?
.Count().ToList()
You're asking it to count the items in the list (which results in a number) and then convert that single number into a list, which makes no sense.
Either return a list and count it later (omit the .Count()) or, change the method to return an int not a List<int> and omit the .ToList()
protected int getManagerDoneCount(string managerName)
{
using (var context = new InfoDBContext())
{
int managerDoneCount = context.InfoSet
.Where(x => x.ww >= wwStartSelected &&
x.ww <= wwEndSelected &&
x.manager == managerName &&
x.status == "Done")
.GroupBy(x => x.ww)
.Count();
return managerDoneCount;
}
}
As an aside, to save you writing hundreds of these methods, you can pass the Where clause in as a parameter...
using System.Linq.Expressions;
protected int getManagerCount(string managerName, Expression<Info> predicate)
{
using (var context = new InfoDBContext())
{
int managerDoneCount = context.InfoSet
.Where(predicate)
.GroupBy(x => x.ww)
.Count();
return managerDoneCount;
}
}
Then call it like this...
var count = getManagerCount("...", x => x.ww >= wwStartSelected &&
x.ww <= wwEndSelected &&
x.manager == managerName &&
x.status == "Done");
Edit Re: Comments
To return a count of each group, List<int> is a bad idea as you aren't ordering the groups so the counts will be in an undefined order. The ideal solution is to have a class that has an appropriate Key and Count property, but to simplify the example, I'll use a Tuple.
//I'm assuming `ww` is an `int`, change the first type parameter of the tuple as appropriate
List<Tuple<int, int>> counts = context.InfoSet
.Where(x => x.ww >= wwStartSelected &&
x.ww <= wwEndSelected &&
x.manager == managerName &&
x.status == "Done")
.GroupBy(x => x.ww)
.Select(x => new Tuple<int, int>(x.Key, x.Count())).ToList();
Note that after you've done the group, the next Select is against the group, which has a Key property for the thing you've grouped on and a lot of aggregate methods for counting, summing, etc..
If you really just want a list of ints, change the last Select to be...
.Select(x => x.Count())
If you weren't passing it out of the method, I'd just use an anonymous class...
.Select(x => new {Ww = x.Key, Count = x.Count()})
But that's no use in a method signature. If you created a CountResult class with Ww and Count properties...
.Select(x => new CountResult{Ww = x.Key, Count = x.Count()})
Edit Re: Comments #2
Linq-To-Entities builds an expression tree which is executed against SQL server, whereas Linq-To-Objects runs in-memory on the client and has more features (as it doesn't need to work out equivalent SQL). In this case, when it gets results from SQL it creates a special proxy class that looks/behaves the same as your entities but handle additional things like tracking which properties have changed. Because of this, you can only use classes which can be constructed (with a parameterless constructor) and then have their properties set (and tracked).
(Although you didn't specify Linq-To-Entities, it's obvious from your question so I should've caught this).
LINQ doesn't deal in lists, but IQueryables, which support lazy evaluation.
Eg If you do...
var a = dbcontext.SomeSet.Where(x => true); //You could omit the Where entirely, just for illustration purposes
var b = a.Where(x => x.Id < 100);
var c = b.ToList();
The query is only executed on the last line and at most 100 records will be returned by the database. a and b are both IQueryable<SomeSet> and "just" contain the expression tree (basically a hierarchy representing the constrains/operations applied so far).
So, to be able to use parameterised constructors / other Linq-To-Object features, we can force the evaluation ...
List<Tuple<int, int>> counts = context.InfoSet
.Where(x => x.ww >= wwStartSelected &&
x.ww <= wwEndSelected &&
x.manager == managerName &&
x.status == "Done")
.GroupBy(x => x.ww)
.ToList() // <<<< Force execution of SQL query
.Select(x => new Tuple<int, int>(x.Key, x.Count())).ToList();
Which should allow you to use constructors, should you wish.
That said, getting a zero count is difficult - the same as it would be getting it from a database (if you group by a field, it doesn't show any 0 counts). There are a number of ways to approach this and which one you use depends on how much data you're playing with. All of them require some way of defining all possible values. I'll use a List<string> as it works well with LINQ
You could, for example get a list of all values and run a different count for each. This is easy but requires multiple queries. If there are lots of groups, it might be expensive...
var groups = new List<string> {"ww1", "ww2", ...};
var counts = groups.Select(g => context.InfoSet.Where(x => x.ww == g &&
x.manager == managerName &&
x.status == "Done").Count());
(This will only return counts, but in the same order as your groups list. As before, you can Select anything you like, including a CountResult...)
var counts = groups.Select(g => new CountResult {
Ww = g,
Count = context.InfoSet.Where(x => x.ww == g &&
x.manager == managerName &&
x.status == "Done").Count();
});
Alternatively, you can run the query you were doing previously and add the missing groups with a count of 0. This has the benefit of running a single SQL query and letting the database do all the heavy lifting (Ok, handling a few counts isn't too expensive but it's worth bearing in mind for other problems - you don't want to get the whole DB table in memory and do the processing there!).
var groups = new List<string> {"ww1", "ww2", ...};
var previousQuery = ... //(I'll assume a List<CountResult> but tuple would work the same)
var finalList = previousQuery.Concat(
groups.Where(g => ! previousQuery.Exists(p => p.Ww == g))
.Select(g => new CountResult {Ww=g, Count=0})
);
In short, take the previous results set, and concatenate (join) it with the result of; (take a list of all groups, remove those already in set 1, for the remainder create a new object with the appropriate ww and a count of 0)
If i have an object of type Photo and a Result set which is sorted in a particular order, is there a way for me to get the position of the current Photo object in the result set. and then get all objects that would follow it?
You can do something like this (not terribly efficient):
var result =
photos.Select((p, i) => new { Index = i, Photo = p })
.SkipWhile(x => x.Photo != photo).Skip(1);
This will give you all photos following photo combined with their index in the original collection.
If you're sorting against an Id:
// gets the previous photo, or null if none:
var previousPhoto = db.Photos
.Where(p => p.Id < currentPhotoId)
.OrderByDescending(p => p.Id)
.FirstOrDefault();
// gets the next photo, or null if none:
var nextPhoto = db.Photos
.Where(p => p.Id > currentPhotoId)
.OrderBy(p => p.Id)
.FirstOrDefault();
If you have custom ordering, you'd need to replace the OrderBy/OrderByDescending expression with your custom ordering. You'd also need to use the same ordering criteria in Where() to get only those photos before or after the current photo.
Not sure if I understand correctly but this might help:
var result = photos.Skip(100); // 100 would be the position of current object.
// if you don't know the index of the current object:
// This won't work on LINQ to SQL directly, do it on a list or something.
var result = photos.SkipWhile(x => x != currentObject).Skip(1);
In reality, if you are dealing with a database, there should be some identifier (a set of columns) you are sorting by. If you want to do the whole thing on the server side, you can grab the properties of the current object and filter the result set specifically for objects that would come after that in the sort order you want.
Index of the photo:
result.IndexOf(photo);
Items after it:
result.SkipWhile((q, i) => i <= result.IndexOf(photo));