Filter a LINQ query - c#

Here's my query.
var query = from g in dc.Group
join gm in dc.GroupMembers on g.ID equals gm.GroupID
where gm.UserID == UserID
select new {
id = g.ID,
name = g.Name,
pools = (from pool in g.Pool
// more stuff to populate pools
So I have to perform some filtering, but when I attempt to filter
var filter = query.Where(f => f.pools.[no access to list of columns]
I can't access any of the items within "pools". Does anyone know how I'm able to access that?
What I'd like to do is this:
var filterbyGame = query.Where(f = > f.pools.GameName == "TestGame");
Let me know if that's even possible with thew ay I have this setup.
Thanks guys.

In your query you can't do Where(f => f.pools.GameName)
because f is an IEnumerable<>
Something like this should work:
Where(f => f.pools.Any(p => p.GameName == "TestGame"))

pools is an enumeration, not a single instance. That's why you're not getting column names.
You need to change your filter to something like:
var filterByGame = query.Where(f => f.pools.Any(p => p.GameName == "TestGame"));

Related

How to write linq query for this sql statement

How would you write a linq query with the following SQL statement. I've tried several methods referenced on stackoverflow but they either don't work with the EF version I'm using (EF core 3.5.1) or the DBMS (SQL Server).
select a.ProductID, a.DateTimeStamp, a.LastPrice
from Products a
where a.DateTimeStamp = (select max(DateTimeStamp) from Products where a.ProductID = ProductID)
For reference, a couple that I've tried (both get run-time errors).
var results = _context.Products
.GroupBy(s => s.ProductID)
.Select(s => s.OrderByDescending(x => x.DateTimeStamp).FirstOrDefault());
var results = _context.Products
.GroupBy(x => new { x.ProductID, x.DateTimeStamp })
.SelectMany(y => y.OrderByDescending(z => z.DateTimeStamp).Take(1))
Thanks!
I understand you would like to have a list of the latest prices of each products?
First of all I prefer to use group by option even over 1st query
select a.ProductID, a.DateTimeStamp, a.LastPrice
from Products a
where a.DateTimeStamp IN (select max(DateTimeStamp) from Products group by ProductID)
Later Linq:
var maxDateTimeStamps = _context.Products
.GroupBy(s => s.ProductID)
.Select(s => s.Max(x => x.DateTimeStamp)).ToArray();
var results = _context.Products.Where(s=>maxDateTimeStamps.Contains(s.DateTimeStamp));
-- all assuming that max datetime stamps are unique
I've managed to do it with the following which replicates the correlated sub query in the original post (other than using TOP and order by instead of the Max aggregate), though I feel like there must be a more elegant way to do this.
var results = from x
in _context.Products
where x.DateTimeStamp == (from y
in _context.Products
where y.ProductID == x.ProductID
orderby y.DateTimeStamp descending
select y.DateTimeStamp
).FirstOrDefault()
select x;
I prefer to break up these queries into IQueryable parts, do you can debug each "step".
Something like this:
IQueryable<ProductOrmEntity> pocoPerParentMaxUpdateDates =
entityDbContext.Products
//.Where(itm => itm.x == 1)/*if you need where */
.GroupBy(i => i.ProductID)
.Select(g => new ProductOrmEntity
{
ProductID = g.Key,
DateTimeStamp = g.Max(row => row.DateTimeStamp)
});
//// next line for debugging..do not leave in for production code
var temppocoPerParentMaxUpdateDates = pocoPerParentMaxUpdateDates.ToListAsync(CancellationToken.None);
IQueryable<ProductOrmEntity> filteredChildren =
from itm
in entityDbContext.Products
join pocoMaxUpdateDatePerParent in pocoPerParentMaxUpdateDates
on new { a = itm.DateTimeStamp, b = itm.ProductID }
equals
new { a = pocoMaxUpdateDatePerParent.DateTimeStamp, b = pocoMaxUpdateDatePerParent.ProductID }
// where
;
IEnumerable<ProductOrmEntity> hereIsWhatIWantItems = filteredChildren.ToListAsync(CancellationToken.None);
That last step, I am putting in an anonymous object. You can put the data in a "new ProductOrmEntity() { ProductID = pocoMaxUpdateDatePerParent.ProductID }...or you can get the FULL ProductOrmEntity object. Your original code, I don't know if getting all columns of the Product object is what you want, or only some of the columns of the object.

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 SQL query with multiple GroupBy columns to LINQ

SELECT
[TimeStampDate]
,[User]
,count(*) as [Usage]
FROM [EFDP_Dev].[Admin].[AuditLog]
WHERE [target] = '995fc819-954a-49af-b056-387e11a8875d'
GROUP BY [Target], [User] ,[TimeStampDate]
ORDER BY [Target]
My database table has the columns User, TimeStampDate, and Target (which is a GUID).
I want to retrieve all items for each date for each user and display count of entries.
The above SQL query works. How can I convert it into LINQ to SQL? Am using EF 6.1 and my entity class in C# has all the above columns.
Create Filter basically returns an IQueryable of the entire AuditLogSet :
using (var filter = auditLogRepository.CreateFilter())
{
var query = filter.All
.Where(it => it.Target == '995fc819-954a-49af-b056-387e11a8875d')
.GroupBy(i => i.Target, i => i.User, i => i.TimeStamp);
audits = query.ToList();
}
Am not being allowed to group by on 3 columns in LINQ and I am also not sure how to select like the above SQL query with count. Fairly new to LINQ.
You need to specify the group by columns in an anonymous type like this:-
var query = filter.All
.Where(it => it.Target == '995fc819-954a-49af-b056-387e11a8875d')
.GroupBy(x => new { x.User, x.TimeStampDate })
.Select(x => new
{
TimeStampDate= x.Key.TimeStampDate,
User = x.Key.User,
Usage = x.Count()
}).ToList();
Many people find query syntax simpler and easier to read (this might not be the case, I don't know), here's the query syntax version anyway.
var res=(from it in filter.All
where it.Target=="995fc819-954a-49af-b056-387e11a8875d"
group it by new {it.Target, it.User, it.TimeStampDate} into g
orderby g.Key.Target
select new
{
TimeStampDate= g.Key.TimeStampDate,
User=g.Key.User,
Usage=g.Count()
});
EDIT: By the way you don't need to group by Target neither OrderBy, since is already filtered, I'm leaving the exact translation of the query though.
To use GroupBy you need to create an anonymous object like this:
filter.All
.Where(it => it.Target == '995fc819-954a-49af-b056-387e11a8875d')
.GroupBy(i => new { i.Target, i.User, i.TimeStamp });
It is unnecessary to group by target in your original SQL.
filter.All.Where( d => d.Target == "995fc819-954a-49af-b056-387e11a8875d")
.GroupBy(d => new {d.User ,d.TimeStampDate} )
.Select(d => new {
User = d.Key.User,
TimeStampDate = d.Key.TimeStampDate,
Usage = d.Count()
} );

Eliminate loop and use Linq instead

Say I have a List as below:
List<R> lstR = GetR();
Now I want a Linq statement to get menus assigned to R, I achieved this by using a loop and then using Linq to get the menus as below:
List<int> ids = new List<int>();
foreach (R r in lstR)
{
ids.Add(r.Id);
}
menu = (from s in db.Menu
where ids.Contains(s.R.Id)
select s.MenuText).Distinct();
Now as far as I know the above is two loop(Linq is using internal loop). would I be able to combine these two statements i.e. not do the first loop to get the ids?
In both lstR and db.Menu are either in-memory data sets (Linq-to-Objects) or IQueryable collections from your database, you can do this:
menu =
(from s in db.Menu
where lstR.Select(r => r.Id)
.Contains(s.R.Id)
select s.MenuText)
.Distinct();
Or this:
menu =
(from s in db.Menu
join r in lstR on s.R.Id equals r.Id
select s.MenuText)
.Distinct();
However, since List<R> exists in memory and db.Menu is an IQueryable, you're options are limited. You could materialize db.Menu into an IEnumerable, so you can process it in memory:
List<R> lstR = GetR();
menu =
(from s in db.Menu.AsEnumerable()
join r in lstR on s.R.Id equals r.Id
select s.MenuText)
.Distinct();
But, this can be costly if there are a lot of records. It's better to do something like this, which admittedly doesn't look much different from what you already have:
List<R> lstR = GetR();
var ids = lstR.Select(r => r.Id).ToList(); // or .ToArray();
menu =
(from s in db.Menu
where ids.Contains(s.R.Id)
select s.MenuText)
.Distinct();
But in truth, the best option is to see if you can refactor GetR so that it returns an IQueryable<R> from your database. That way you can use both of the first two options without needing to materialize any sets into memory first. And by the way, once you've done that and set up navigation properties, you can probably do something like this:
IQueryable<R> lstR = GetR();
menu =
(from r in lstR
from s in r.Menus
select s.MenuText)
.Distinct();
It can be done like.
menu = (from s in db.Menu
where lstR.Select(item => item.Id).Contains(s.R.Id)
select s.MenuText).Distinct();
But i wouldnt combine those two statements, because if you use a HashSet it will speed up:
var ids = new HashSet<int>(lstR);
menu = (from s in db.Menu
where ids.Contains(s.R.Id)
select s.MenuText).Distinct();
This will be faster i guess. The problem with the first one is, every s in db.Menu The list is iterated for creating a list of id's select().
You coud use the linq projection method Select():
ids = lstR.Select(p => p.Id);
menu = db.Menu.Where(s => GetR().Select(r => r.Id).Contains(s.R.Id))
.Select(s => s.MenuText)
.Distinct();
but it will be to complex. It will be better if you'l write like this
var ids = GetR().Select(r => r.Id);
menu = db.Menu.Where(s => ids.Contains(s.R.Id))
.Select(s => s.MenuText)
.Distinct();
Use Join
var result = (from s in db.Menu
join r in lstR on s.Id equals r.ID
select s.MenuText).Distinct();

Filter LINQ entity include query

I have the following two tables
Groups
Id (int)
People
Id (int)
GroupId (int, Groups.Id)
IsSelected (bit)
This will return all Groups with all their members(People) in a single query
var grps = myDatabase.Groups.Include("People");
How can I write a single query that will return all Groups with People who has been selected(IsSelected = true)?
let me know if this works
var grps = myDatabase.Groups.Select(g=> new { g, people = g.People.Where(p=>p.IsSelected)});
You will want to use the 'join' method, like this:
(from g in myDatabase.Groups
join p in myDatabase.People on g.Id equals p.GroupId
where p.IsSelected == true
select g);
This will give you all groups where there are people selected.
OR check out .Where()
Something like
var grps = myDatabase.Groups.Include("People").Where(x => x.IsSelected);
//x => !x.IsSelected for false

Categories