When building LINQ query, try do decompose parts for building final query.
var positions =
from dp in ctx.CCDailyPositions
group by new { dp.masterline_id } into g
select new
{
ID = g.Max(x => x.ID),
dp.masterline_id
};
var purchases =
from pr in ctx.CCPurchase
group by new { pr.masterline_id } into g
select new
{
ID = g.Max(x => x.ID),
pr.masterline_id
};
var query =
from m in ctx.CCMemberizationPII
from dp in positions.Where(dp => dp.masterline_id == m.application_id)
.DefaultIfEmpty()
from pr in purchases.Where(pr => pr.masterline_id == m.application_id)
.DefaultIfEmpty()
orderby m.Id
select new
{
m.Id,
m.borrower_id,
m.application_id,
m.borrower_first_name,
m.borrower_last_name,
DP_ID = dp.ID,
PR_ID = pr.ID,
};
Also your query can be written using OUTER APPLY. Note that EF Core can transform OUTER APPLY to LEFT JOIN with Window Function usage.
var query =
from m in ctx.CCMemberizationPII
from dp in ctx.CCDailyPositions.Where(dp => dp.masterline_id == m.application_id)
.OrderByDescending(dp => dp.ID)
.Take(1)
.DefaultIfEmpty()
from pr in ctx.CCPurchase.Where(pr => pr.masterline_id == m.application_id)
.OrderByDescending(pr => pr.ID)
.Take(1)
.DefaultIfEmpty()
orderby m.Id
select new
{
m.Id,
m.borrower_id,
m.application_id,
m.borrower_first_name,
m.borrower_last_name,
DP_ID = dp.ID,
PR_ID = pr.ID,
};
Which SQL will be better, depends on execution plan in your particular case.