Convert multiple PARTITION BY to LINQ - c#

EF Core does not have a SqlQuery() method yet (epic fail) so I have no way to run a query/stored proc on my database to get "summary" data. Here is a summary query I have with multiple PARTITION BYs that I need to convert to LINQ:
SELECT
s.Name,
SUM(CASE WHEN pc.WorkflowStateId <> 99 THEN 1 ELSE 0 END)
OVER (PARTITION BY s.SiteId) 'RegisteredCount',
SUM(CASE WHEN pc.WorkflowStateId = 99 THEN 1 ELSE 0 END)
OVER (PARTITION BY s.SiteId) 'ScreenFailedCount'
FROM Sites s
JOIN Patients p ON p.SiteId = s.SiteId
JOIN PatientCycles pc ON pc.PatientId = p.PatientId
Sites, Patients, and PatientCycles are DbSets.
How can I convert this to a C# LINQ query?
UPDATE
This is the solution I came up with:
var summaries = from site in context.Sites
let registered = (from pc in context.PatientCycles
where site.SiteId == pc.Patient.SiteId && pc.WorkflowStateId != WorkflowStateType.Terminated
select pc).Count()
let terminated = (from pc in context.PatientCycles
where site.SiteId == pc.Patient.SiteId && pc.WorkflowStateId == WorkflowStateType.Terminated
select pc).Count()
select new SiteSummary { Site = site, RegisteredCount = registered, ScreenFailedCount = terminated };

Well an alternative in Linq would be something like this:
var query= from s in context.Sites
join p in context.Patiens on s.SiteId equals p.SiteId
join pc in context.PatientCycles on p.PatiendId equals p.PatiendId
group pc.WorkflowStateId by new{s.SiteId,s.Name} into g
select new {Name=g.Key.Name,
RegisteredCount=g.Sum(e=>e!=99?1:0),
ScreenFailedCount=g.Sum(e=>e==99?1:0)
}
Update
To avoid that kind of problem use nav. properties:
var query= from s in context.Sites
select t new SiteSummary
{
SiteId = s.SiteId,
Name = s.Name,
Code = s.Code,
RegisteredCount =s.Patiens.SelectMany(e=>e.PatientCycles.Select(y=>y.WorkflowStateId ))
.Sum(x => x!= WorkflowStateType.Terminated ? 1 : 0),
ScreenFailedCount = s.Patiens.SelectMany(e=>e.PatientCycles.Select(y=>y.WorkflowStateId ))
.Sum(x => x== WorkflowStateType.Terminated ? 1 : 0)
};
Also I suggest to initialize the collection navigation properties in entity constructors:
public class Sites
{
//...
public virtual ICollection<Patient> Patients{get;set;}
public Sites()
{
Patients=new List<Patient>();
}
}

Related

How to handle null values in linq expression with joins? [duplicate]

This question already has answers here:
Sequence contains no elements?
(7 answers)
Closed 10 months ago.
I try to get some data from few table with joining. This query is give an error as System.InvalidOperationException , Message=" Sequence contains no elements ". How can I avoid this error.
var assignments = (from s in _db.SubmissionLinks
join a in _db.Assignments on s.AssignmentID equals a.AssignmentID
join p in _db.Projects on a.ProjectID equals p.ID
join s2 in _db.SystemUsers on p.SystemUserFK equals s2.ID
select new AssignmentViewModel()
{
SubmissionlinkID = s.SubmissionLinkID,
SubmissionlinkName = s.SubmissionName,
ProjectID = p.ID,
ProjectName = p.ProjectName,
Deadline = s.Deadline,
Userid = s2.ID,
Assignmentid = a.AssignmentID,
IsActive = s.ActiveStatus
}).Where(s =>s.Userid == ID && s.IsActive == 0).Distinct().First();
I try to avoid that by checking returning objects inside of the where clause but it didn't work
var assignments = (from s in _db.SubmissionLinks
join a in _db.Assignments on s.AssignmentID equals a.AssignmentID
join p in _db.Projects on a.ProjectID equals p.ID
join s2 in _db.SystemUsers on p.SystemUserFK equals s2.ID
select new AssignmentViewModel()
{
SubmissionlinkID = s.SubmissionLinkID,
SubmissionlinkName = s.SubmissionName,
ProjectID = p.ID,
ProjectName = p.ProjectName,
Deadline = s.Deadline,
Userid = s2.ID,
Assignmentid = a.AssignmentID,
IsActive = s.ActiveStatus
}).Where(s => s != null && s.Userid == ID && s.IsActive == 0).Distinct().First();
Agree with Panagiotis; its supposed to be that SubmissionLink has a .Assignment property that returns the parent Assignment, Assignment has a .Project prop that returns the Project etc, and then you just do:
_db.SubmissionLinks
.Where(s =>s.Userid == ID && s.IsActive == 0)
.Select(s => s.Select(s => new AssignmentViewModel()
{
SubmissionlinkID = s.SubmissionLinkID,
SubmissionlinkName = s.SubmissionName,
ProjectID = s.Assignment.Project.ID,
ProjectName = s.Assignment.Project.ProjectName,
Deadline = s.Deadline,
Userid = s.Assignment.Project.SystemUser.ID,
Assignmentid = s.Assignment.AssignmentID,
IsActive = s.ActiveStatus
})
.SomethingThatRunsTheQueryLike_First_FirstOrDefault_ToList_etc
EF works out the rest from how it seens you use the navigations (s.Assignment.Project.ProjectName etc). Starting from the many end and going up to the one end is quite easy, because it's just a single object at the end of the nav. Going down through collections is a bit more of a nuisance, so think carefully about where you start your query from for the easiest life. For example if you wanted the Project and its most recent submission, you might:
db.Projects.Select(p => new Something{
p.ProjectName,
MostRecentLinkId = p.SubmissionLinks.OrderByDescending(sl => sl.CreatedDate).First().Id
})
That p.SubmissionLinks.OrderByDescending(sl => sl.CreatedDate).First().Id would be translated (by recent EFC) to something like ... ROW_NUMBER() OVER(PARTITION BY projectid ORDER BY createddate DESC) rn ... WHERE rn=1 in a subquery, but working with collections is usually that little more tricky because you're performing aggregations to make them make sense in the context of a single parent. If you can start from a different place so youre not digging into collections it's simpler

Filtering a IQueryable with results from another table

I have an EF query which gets products from the database.
var query = (from pPrice in db.ProductPricing
join prod in db.Products on pPrice.ProductID equals prod.ProductID
join productExt in db.ProductsExt on prod.ProductID equals productExt.ProductID into pExts
from prodExt in pExts.DefaultIfEmpty()
where (includeNonPublic || pPrice.ShowOnline == 1)
&& ((eventID.HasValue && pPrice.EventID == eventID) || (!eventID.HasValue && !pPrice.EventID.HasValue))
orderby prod.DisplayOrder
select new ProductPricingInfo()
{
Product = prod,
ProductPricing = pPrice,
ProductExtension = prodExt
});
I have a table where I can specify add-on products (products which can be bought once the parent item has been bought).
My query to fetch these add-on products is
var addOnProductsQuery = (from pa in db.ProductAddons
where pa.EventID == eventID && pa.StatusID == 1
select new { ProductID = pa.ChildProductId });
Now what I am trying to do is filter on the query variable to only return products which are not in the addOnProductsQuery result.
Currently I have
var addOnProducts = addOnProductsQuery.ToList();
query = query.Where(e => !addOnProducts.Contains(e.Product.ProductID));
But there is a syntax error on the Contains(e.Product.ProductID)) statement
Argument 1: cannot convert from int to anonymous type: int ProductID
Chetan is right in the comments, you need to select the integer from the object before using Contains.
You can do it in 2 ways:
First you could just take the integer initially:
var addOnProductsQuery = (from pa in db.ProductAddons
where pa.EventID == eventID && pa.StatusID == 1
select new pa.ChildProductId);
This should give int as type so you can use Contains later with no problem.
Second, if you want to keep the addOnProductsQuery unchanged:
var addOnProducts = addOnProductsQuery.Select(a => a.ProductID).ToList();
query = query.Where(e => !addOnProducts.Contains(e.Product.ProductID));

How can we perform a select into another select using Linq c# left join, count , sum, where

How can I convert my sql code into Linq format :
How can we perform a select into another select using Linq c# :
My sql code :
SELECT DepartementProjects.Label, a.Number FROM
(SELECT DepartementProjects.Label ,count(1) as Number FROM DepartementProjects
inner join Absences on DepartementProjects.Id = Absences.DepartementProjectID
where Absences.Profil='SHT'
group by DepartementProjects.Label ) a right join DepartementProjects on DepartementProjects.Label = a.Label;
My attempt :
var AbsByDepartmentADM = from department in _dbContext.DepartementProjects
join abs in _dbContext.Absences on department.Id equals abs.DepartementProjectID
into groupedResult
from groupedResultRight in groupedResult.DefaultIfEmpty()
group groupedResultRight by department.Label into grouped
let NumberOfAbsence = grouped.Count(t => t.DepartementProjectID != null)
let WorkedHours = grouped.Sum(a => a.WorkedHours != null ? a.WorkedHours : 0)
select new
{
DepartmentId = grouped.Key,
NumberOfAbsence,
WorkedHours,
AbsencesHours = (8 * NumberOfAbsence - WorkedHours),
};
If you are trying to translate SQL, translate any subselects first, then the main select, and translate in LINQ phrase order. Also, your LINQ adds some values not in the SQL, so I didn't try to change the SQL to match:
var sub = from dept in _dbContext.DepartementProjects
join abs in _dbContext.Absences on dept.Id equals abs.DepartementProjectID into absj
from abs in absj
where abs.Profil == "SHT"
group abs by dept.Label into absg
select new { Label = absg.Key, Number = absg.Count() };
var ans = from dept in _dbContext.DepartementProjects
join a in sub on dept.Label equals a.Label into lnj
from ln in lnj.DefaultIfEmpty()
select new { dept.Label, ln.Number };

linq join incorrect syntax error

I am using LINQ query to get the rows with certain conditions. Here is the query.
var query = from v in dt1.AsEnumerable()
join c in dt2.AsEnumerable() on v.Field<int>("ID") equals c.Field<int>("ID")
where v.Field<string>("col1").Equals("abcd")
&& (c.Field<string>("col1").Equals("8776") || c.Field<string>("col1").Equals("8775"))
select new
{
ok = (from a in v where v.Field<string>("stah").Equals("1") select a).count(),
ok1 = (from a in v where v.Field<string>("stah").Equals("2") select a).count(),
ok2 = (from a in v where v.Field<string>("stah").Equals("3") select a).count()
};
The error is present in
ok = (from a in v where v.Field<string>("stah").Equals("1") select a).count()
The error is
could not find an implementation of the query pattern for source type
'system.data.DataRow'. 'Where' not found
Sample Input :
dt1
iD col1 stah
1 4567 1
2 8748 2
3 3487 3
4 8776 1
dt2
iD col1
1 4754
2 4576
Output
Get count of all rows where stah=1 && dt2.col1='4754'
But I cannot get it working. What is the correct syntax for this ?
If I have understood you correctly, then this is what you need:-
var query = dt1.AsEnumerable()
.Where(x => x.Field<int>("stah") == 1
&& dt2.AsEnumerable()
.Any(z => z.Field<int>("Id") == x.Field<int>("Id")
&& z.Field<string>("Col1") == "4754")
).Count();
#HarshitShrivastava mentioned that my previous attempt at the query didn't take into account all the where conditions.
How about this version using a mix of Linq query and Linq Lambda:
var query = from dataRows1 in dt1.AsEnumerable().Where(r => r.Field<string>("col1").Equals("abcd"))
join dataRows2 in dt2.AsEnumerable().Where(r => r.Field<string>("col1").Equals("8776") || r.Field<string>("col1").Equals("8775"))
on dataRows1.Field<int>("ID") equals dataRows2.Field<int>("ID") into b
select new
{
id = dataRows1.Field<int>("ID"),
ok = (from a in b where a.Field<string>("stah").Equals("1") select a).Count(),
ok1 = (from a in b where a.Field<string>("stah").Equals("2") select a).Count(),
ok2 = (from a in b where a.Field<string>("stah").Equals("3") select a).Count()
};
Note: I included the ID field in the projected output just for verifying the results. Remove as needed.

Access data from indirect child table using LInq

I have following table in my database and im accessing them through EF.
TestPack { id, name, type }
Sheets{ id, tp_id, date, rev }
Spool { id, sheet_id, date, rev, fabricated, installed }
which means a test pack has 1-M sheets and each sheet has 1-M spools. I want to get count of total Spools in the Test Pack, and count of spools that are fabricated, count of spools that are installed.
How do I get that through Linq query?
if I understand you right,you would like to have something like this
(from tp in ctx.TestPack
join st in ctx.Sheets on st.tp_id equals tp.id
join sl in ctx.Spool on sl.steet_id equals st.id
where tp.id == testPackId //you can change or delete this condition
select new {
Total = sl.Count() ,
FabricatedSpools = sl.Count(x=>x.fabricated == true),
InstalledSpools = sl.Count(x=>x.installed == true)
}).FisrtOrDefault();
Or maybe
(from tp in ctx.TestPack
join st in ctx.Sheets on st.tp_id equals tp.id
join sl in ctx.Spool on sl.steet_id equals st.id
where tp.id == testPackId //you can change or delete this condition
select new {
Total = sl.Count() ,
FabricatedSpools = (from s in sl
where s.fabricated == true
select s.Count()),
InstalledSpools = (from i in sl
where i.installed== true
select i.Count()),
}).FisrtOrDefault();
Not sure what you exact models are like but see below.
var testPackID = 2;//assuming
//assuming your DbContext is ctx;
var totalSpools = ctx.Spools.Count(x => x.Sheets.tp_id == testPackID);
var fabricatedSpools = ctx.Spools.Count(x => x.Sheets.tp_id == testPackID && x.fabricated);
var installedSpools = ctx.Spools.Count(x => x.Sheets.tp_id == testPackID && x.installed);
Sample data and query
I had generated the queries in SQL Server and hope you can do in LINQ. if you want specifically in LINQ let me know.
And can you please clarify whether you want result in 3 or all the 3 results in one.
Hope this helps.
Thank you.

Categories

Resources