Filtering child collection with subquery - c#

I have a query in sql, as shown in the code below:
select *
from Registration r
inner join RegistrationService rs on rs.RegistrationID = r.RegistrationID
inner join Service s on s.ServiceID = rs.ServiceID
where cast(RegistrationDate as DATE) between #startDate and #endDate
and s.ByDoctor = 'false'
and rs.ServiceID not in (select ServiceID from TreatmentService ts where ts.TreatmentID = r.RegistrationID)
Now I have to convert this into linq syntax because I'm using EF as my data access. I'm getting a problem when converting the last line:
rs.ServiceID not in (select ServiceID from TreatmentService ts where ts.TreatmentID = r.RegistrationID)
and my linq syntax:
var query = context.Registrations.Where(r =>
DbFunctions.TruncateTime(r.RegistrationDate) == DbFunctions.TruncateTime(DateTime.Today)
&&
r.RegistrationServices.Any(rs => rs.Service.ByDoctor == false)
&&
!(context.TreatmentServices.Select(ts => ts.ServiceID).Where(ts => ts.TreatmentID == r.RegistrationID)).Contains(rs.ServiceID) <-- here is the problem
);
How to solve this?

Why not use the linq query-like syntax?
from r
in context.Registration
join rs in context.RegistrationService on rs.RegistrationID = r.RegistrationID
join s in context.Service on s.ServiceID = rs.ServiceID
...
Taken from: LINQ query examples

from r
in context.Registration
join rs in context.RegistrationService on rs.RegistrationID equals r.RegistrationID
join s in context.Service on s.ServiceID equals rs.ServiceID
where s.Where(ts=>ts.TreatmentID == r.RegistrationID).All(ts => ts.ServiceID != rs.ServiceID )

Related

How to add less than or equal to condition in linq inner join

We have two objects, Dates and ActiveEvents. Want to perform inner join on these with less than or equal to condition in linq. Same as ref of below SQL where consider #Tables are C# objects
Select A. from #Activities A
Inner Join #Dates D ON A.ActivityDate <= D.ProcessDate
Tried with below but it's not giving correct results.
var filteredActivity = (from e in ActiveEvents
from p in dates
where e.ActivityDate <= p.Date
select new ActiveEvent
{
ActivityDate = p.Date,
EventId = e.EventId
}).ToList();
And
var filteredActivity = (from e in ActiveEvents
from p in dates.Where(r => e.ActivityDate <= r)
select new ActiveEvent
{
ActivityDate = p.Date,
EventId = e.EventId
}).ToList();
Can you please suggest any better way to do this?
You can try this way
var filteredActivity = (from e in ActiveEvents
join p in dates
where e.ActivityDate <= p.ProcessDate
select new ActiveEvent
{
ActivityDate = p.Date,
EventId = e.EventId
}).ToList();
P/s: Ideally, between 2 tables should contain the foreign key to join like this join p in dates on e.Key equals p.ForeignKey
Based on your example, the query is filtering on ProcessDate but your linq query is filtering on p.Date. Are those the same field? The first example you gave should be correct.

Linq 'Unable to process the type 'Anonymous type', because it has no known mapping to the value layer.' in MVC

I am trying to convert a SQL Command to Linq but gets the error 'Unable to process the type 'Anonymous type', because it has no known mapping to the value layer.'
SQL Code
SELECT ooe.id, parts.title, appro.totalAppro, allo.TotalAllotment, cost.total_cost, (CASE WHEN cost.total_cost IS NULL THEN (SELECT allo.TotalAllotment) ELSE (SELECT allo.TotalAllotment - cost.total_cost) END) AS unobligated
FROM pmTA_OoeGeneral AS ooe INNER JOIN
pmTA_Particulars AS parts ON ooe.particular_id = parts.id LEFT OUTER JOIN
pmTA_vwObligationRequestDetailsTotalCostByOoe AS cost ON ooe.id = cost.ooe_general_id
LEFT OUTER JOIN pmTA_vwTotalAllotmentGeneralFund AS allo ON ooe.id = allo.ooe_general_id
LEFT OUTER JOIN pmTA_vwTotalAppropriationGeneralFund AS appro ON ooe.id = appro.ooe_general_id
WHERE (ooe.year = #year) AND (ooe.project_category_id = #cat) and (ooe.type = #type) order by ooe.id
LINQ Code
var gvStatusGenOffices = (from a in db.iBudget_OoeGeneral
join b in db.iBudget_Particulars on a.ParticularID equals b.id
join c in obligationRequestDetailsTotalCostByOoe on a.id equals c.id
join d in totalAllotmentGeneralFund on a.id equals d.id
join e in totalAppropriationGeneralFund on a.id equals e.id
where a.YearID==year && a.ProjectID==cat && a.type==type
orderby a.id
select new
{
id = a.id,
title = b.title,
appro = e.totalAppro,
allo = d.totalAllo,
cost = c.totalCost,
unobligated = c.totalCost==null ? d.totalAllo : d.totalAllo - c.totalCost
}).ToList();
db.iBudget_OoeGeneral and db.iBudget_Particulars are model class in my project
obligationRequestDetailsTotalCostByOoe , totalAllotmentGeneralFund and totalAppropriationGeneralFund are variables with list values.
The question is what is causing this error? I tried looking the web for some answers but none was showing up.
UPDATE
var obligationRequestDetailsTotalCostByOoe = (from p in db.iBudget_ObligationRequestDetails
where p.ooe_general_id != null && p.is_approved == 1
group p by p.ooe_general_id into g
select g)
.AsEnumerable()
.Select(g => new {
id = g.Key,
totalCost = g.Sum(p => Convert.ToDouble(p.amount))
}).ToList();
Originally, obligationRequestDetailsTotalCostByOoe , totalAllotmentGeneralFund and totalAppropriationGeneralFund were Views in SQL Server. Since I was practicing how to code Linq, I didn't create a View and just made a var with list same with the original View Code in SQL.

Linq query in EF Core 2 when using joins and pagination order by column alias doesn't work

We are currently trying to write out sorting into our server-side pagination using Linq and EF Core 2. We are running into an issue where the column alias being produced by Linq does not work while using pagination. However if we do not paginate it works as intended.
All of the columns within the outputted queries are aliases as we have different property names in the model and database column names are different, but this shouldn't make a difference to our knowledge.
This is the Linq query without the pagination:
var source = from p in _ppmRepository.GetAll()
join jt in _jobTypeRepository.GetAll() on p.PpmFkeyInSeq equals jt.Id into jtdata
from jt in jtdata.DefaultIfEmpty()
join a in _assetRepository.GetAll() on p.PpmFkeyArSeq equals a.Id into aData
from a in aData.DefaultIfEmpty()
where p.PpmFkeyBgSeq == bldId
orderby p.PpmFreq
select new BuildingPpmListViewModel
{
PpmId = p.Id,
PpmFreq = p.PpmFreq,
PpmNextService = p.PpmNextService,
TotalCost = p.TotalCost,
PpmPeriodUnits = p.PpmPeriodUnits,
PpmFkeyPriDesc = p.PpmFkeyPriDesc,
JtTitle = jt.JtTitle,
AssetId = p.PpmFkeyArSeq,
AssetDescription = a.AssetDescription,
IsDeleted = p.IsDeleted
};
source = source.Where(i => i.JtTitle.Contains("audit") && i.AssetDescription.Contains("df"));
This is the outputted query produced by ef core which works:
SELECT [p].[PPM_SEQ] AS [PpmId], [p].[PPM_FREQ] AS [PpmFreq], [p].[PPM_NEXT_SERVICE] AS [PpmNextService],
CAST([p].[TotalCost] AS float) AS [TotalCost], [p].[PPM_PERIOD_UNITS] AS [PpmPeriodUnits], [p].[PPM_FKEY_PRI_DESC] AS [PpmFkeyPriDesc],
[t].[jt_title] AS [JtTitle], [p].[PPM_FKEY_AR_SEQ] AS [AssetId], [t0].[AR_DESCRIPTION] AS [AssetDescription], [p].[Deleted] AS [IsDeleted]
FROM [PPMs] AS [p]
LEFT JOIN (
SELECT [j].*
FROM [JobTypes] AS [j]
) AS [t] ON [p].[PPM_FKEY_IN_SEQ] = [t].[jt_seq]
LEFT JOIN (
SELECT [a].*
FROM [Assets] AS [a]
) AS [t0] ON [p].[PPM_FKEY_AR_SEQ] = [t0].[ar_seq]
WHERE ([p].[PPM_FKEY_BG_SEQ] = 172) AND ((CHARINDEX(N'audit', [t].[jt_title]) > 0) AND (CHARINDEX(N'df', [t0].[AR_DESCRIPTION]) > 0))
ORDER BY [PpmFreq]
This is the Linq query with the pagination:
var source = from p in _ppmRepository.GetAll()
join jt in _jobTypeRepository.GetAll() on p.PpmFkeyInSeq equals jt.Id into jtdata
from jt in jtdata.DefaultIfEmpty()
join a in _assetRepository.GetAll() on p.PpmFkeyArSeq equals a.Id into aData
from a in aData.DefaultIfEmpty()
where p.PpmFkeyBgSeq == bldId
orderby p.PpmFreq
select new BuildingPpmListViewModel
{
PpmId = p.Id,
PpmFreq = p.PpmFreq,
PpmNextService = p.PpmNextService,
TotalCost = p.TotalCost,
PpmPeriodUnits = p.PpmPeriodUnits,
PpmFkeyPriDesc = p.PpmFkeyPriDesc,
JtTitle = jt.JtTitle,
AssetId = p.PpmFkeyArSeq,
AssetDescription = a.AssetDescription,
IsDeleted = p.IsDeleted
};
source = source.Where(i => i.JtTitle.Contains("audit") && i.AssetDescription.Contains("df")).Skip(0).Take(50);
This is the output of the pagination where in the over function order by PpmFreq is the alias of [p].[PPM_FREQ] that SQL can not find:
SELECT [t1].[PpmId], [t1].[PpmFreq], [t1].[PpmNextService], [t1].[TotalCost], [t1].[PpmPeriodUnits],
[t1].[PpmFkeyPriDesc], [t1].[JtTitle], [t1].[AssetId], [t1].[AssetDescription], [t1].[IsDeleted]
FROM (
SELECT [p].[PPM_SEQ] AS [PpmId], [p].[PPM_FREQ] AS [PpmFreq], [p].[PPM_NEXT_SERVICE] AS [PpmNextService],
CAST([p].[TotalCost] AS float) AS [TotalCost], [p].[PPM_PERIOD_UNITS] AS [PpmPeriodUnits], [p].[PPM_FKEY_PRI_DESC] AS
[PpmFkeyPriDesc], [t].[jt_title] AS [JtTitle], [p].[PPM_FKEY_AR_SEQ] AS [AssetId], [t0].[AR_DESCRIPTION] AS [AssetDescription],
[p].[Deleted] AS [IsDeleted], ROW_NUMBER() OVER(ORDER BY [PpmFreq]) AS [__RowNumber__]
FROM [PPMs] AS [p]
LEFT JOIN (
SELECT [j].*
FROM [JobTypes] AS [j]
) AS [t] ON [p].[PPM_FKEY_IN_SEQ] = [t].[jt_seq]
LEFT JOIN (
SELECT [a].*
FROM [Assets] AS [a]
) AS [t0] ON [p].[PPM_FKEY_AR_SEQ] = [t0].[ar_seq]
WHERE (([p].[PPM_FKEY_BG_SEQ] = 172)) AND ((CHARINDEX(N'audit', [t].[jt_title]) > 0)
AND (CHARINDEX(N'df', [t0].[AR_DESCRIPTION]) > 0))
) AS [t1]
WHERE ([t1].[__RowNumber__] > 0) AND ([t1].[__RowNumber__] <= (50))
This looks to be where our issues are coming from as we can slightly modify it to get a correct result from the database:
ROW_NUMBER() OVER(ORDER BY [PpmFreq]) AS [__RowNumber__]
If we were to modify the above statement to also include the table alias as [p].[PPM_FREQ], like so: ROW_NUMBER() OVER(ORDER BY [p].[PPM_FREQ]) AS [__RowNumber__] then our issues are resolved, but that doesnt seem possible with our current linq query.
See if following works better :
var source = (from p in _ppmRepository.GetAll()
join jt in _jobTypeRepository.GetAll() on p.PpmFkeyInSeq equals jt.Id into jtdata
from jt in jtdata.DefaultIfEmpty()
join a in _assetRepository.GetAll() on p.PpmFkeyArSeq equals a.Id into aData
from a in aData.DefaultIfEmpty()
select new BuildingPpmListViewModel
{
PpmId = p.Id,
PpBgSeq = p.PpmFkeyBgSeq,
PpmFreq = p.PpmFreq,
PpmNextService = p.PpmNextService,
TotalCost = p.TotalCost,
PpmPeriodUnits = p.PpmPeriodUnits,
PpmFkeyPriDesc = p.PpmFkeyPriDesc,
JtTitle = jt.JtTitle,
AssetId = p.PpmFkeyArSeq,
AssetDescription = a.AssetDescription,
IsDeleted = p.IsDeleted
})
.Where(x => x.PpBgSeq == bldId)
.OrderBy(x => x.PpmFreq)
.ToList();
This is a known issue of that we have later filed with the ef core team directly.
This is a known issue which has been fixed for upcoming release of 2.1
You can see more details and possible work-around here
github.com/aspnet/EntityFrameworkCore/issues/9535`
Smit Patel
If you run a nightly build you can fix the above issue.

left join linq query in FF4

I have the following SQL Query:
SELECT DISTINCT *
FROM Documents d
LEFT JOIN
Invoices
ON (i.invoicedocumentid = d.id or i.jobsheetdocumentid = d.id)
INNER JOIN
PurchaseOrders PO ON i.poid = PO.id
LEFT JOIN
HelpDeskFaults f
ON f.id = PO.helpDeskFaultId
LEFT JOIN stores s
ON s.id = f.storeid
WHERE s.name = 'Linden Drive'
OR d.id in (
SELECT u.id as 'docid'
FROM documents u
INNER JOIN stores s
ON u.storeid = s.id
WHERE s.name = 'Linden Drive'
)
ORDER BY d.Id
So far my linq query looks like this:
var documents = from doc in context.Documents
from invoice in context.Invoices
join po in context.PurchaseOrders on invoice.PurchaseOrder.PurchaseOrderId equals po.PurchaseOrderId
join hdf in context.HelpDeskFaults on po.HelpdeskFaultId equals hdf.ID into hdfpo
from hs in hdfpo.DefaultIfEmpty()
join store in context.Stores on hs.StoreID equals store.ID into hsstore
from hss in hsstore.DefaultIfEmpty()
where hss.Name.Contains(jobSearchParams.StoreName) && (invoice.InvoiceDocumentId == doc.ID || invoice.JobSheetInvoiceId == doc.ID)
select doc;
But the sql that is produced is nothing like what I expected.
Can anyone give me any pointers of how to improve my linq query.
This is what I would translate your SQL query into
var results =
(from doc in context.Documents
from invoice in (
from inv in context.Invoices
where inv.InvoiceDocumentId == doc.ID || inv.JobSheetInvoiceId == doc.ID
select inv).DefaultIfEmpty()
from hs in invoice.PurchaseOrder.HelpDeskFaults.DefaultIfEmpty()
from hss in hs.Stores.DefaultIfEmpty()
where hss.Name == "Linden Drive" || doc.Store.Name == "Linden Drive"
order by doc.ID
select new
{
Document = doc,
Invoice = invoice,
invoice.PurchaseOrder,
HelpDeskFault = hs,
Store = hss,
}).Distinct();
I included that anonymous class of the 5 entities because your SQL is using Select *. I would recommend changing it to only return the minimum required set of fields.

Converting left outer join on two queries to LINQ

I have below sql query which I want to convert into LINQ to obtain exactly same results and returned by below query
select *
from (
select distinct DocID
from UserViewDoc
where UserViewDoc.UVID in (102558)) a
left outer join
(
select distinct UserViewDoc.DocID
from UserViewDoc
inner join UserViewHeader on UserViewDoc.UVID = UserViewHeader.UVID
where UserViewDoc.UVID not in (102558)
and UserViewHeader.IsLock = 1) b on a.DocID = b.DocID
where b.DocID is null
)
What I have tried so far is below LINQ statement
var v = (from uvd in this.ViewSelectorControl.LDReviewContext.GetTable<UserViewDoc>()
where IDs.Contains(uvd.UVID)
select new { uvd.DocID, uvd.UVID });
var c = ((from uvd in this.ViewSelectorControl.LDReviewContext.GetTable<UserViewDoc>()
join uvh in this.ViewSelectorControl.LDReviewContext.GetTable<UserViewHeader>() on uvd.UVID equals uvh.UVID
where !IDs.Contains(uvh.UVID) && uvh.IsLock == true
select new { uvd.DocID, uvd.UVID } ));
var d = (from id in v
join ids in c on id.UVID equals ids.UVID into vc
from sub in vc.DefaultIfEmpty()
where sub == null
select id);
The problem I am facing is running the SQL query is returning 30583 records and LINQ version of it is returning all of the 30613 records
I rewrote the query to be exactly like the sql query, i believe that this is the way:
var firstQuery = (from u in this.ViewSelectorControl.LDReviewContext.GetTable<UserViewDoc>()
where IDs.Contains(u.UVID)
select u.DocID).Distinct();
var innerQuery = (from u in this.ViewSelectorControl.LDReviewContext.GetTable<UserViewDoc>()
join uvh in this.ViewSelectorControl.LDReviewContext.GetTable<UserViewHeader>() on uvh.UVID equals u.UVID
where IDs.Contains(u.UIVD) == false
&& uvh.IsLock
select u.DocID).Distinct();
var resultQuery = from f in firstQuery
join i in innerQuery on i equals f into lout
from i in lout.DefaultIfEmpty()
where i == null
select f;

Categories