Linq Limit/Group by Id when joining IQueryables - entity-framework

I have a list of ids, that I then have multiple IQueryables that at the end I am joining to return my results. However some of these queries could have multiple records for that particular ID and I only need 1 record per ID from my set of x IDs. For example there could be 1 Name record, 3 Addresses, 2 Emails, 4 Phones for a single ID, but I only need 1 of each, does not matter which if multiple exist.
var clientNames = (
from n in db.Names
join cnl in db.ClientNameLinks on n.name_id equals cnl.name_id
where request.Ids.Contains(cnl.client_id)
&& n.name_id > 0
&& n.nametype_id != null
select new
{
n.display_name,
cnl.client_id
});
var clientAddresses = (
from a in db.Addresses
join cal in db.ClientAddressLinks on a.address_id equals cal.address_id
where request.Ids.Contains(cal.client_id)
&& a.address_id > 0
select new {
a.display_address,
cal.client_id
});
var clientEmails = (
from e in db.Emails
join cel in db.ClientEmailLinks on e.email_id equals cel.email_id
where request.Ids.Contains(cel.client_id)
&& e.email_id > 0
select new {
e.email_address,
cel.client_id
});
var clientPhones = (
from p in db.Phones
join cpl in db.ClientPhoneLinks on p.phone_id equals cpl.phone_id
where request.Ids.Contains(cpl.client_id)
&& p.phone_id > 0
select new {
p.phone_num,
cpl.client_id
});
var rows = await (
from n in clientNames
join a in clientAddresses on n.client_id equals a.client_id into n_a
from na in n_a.DefaultIfEmpty() //LEFT JOIN
join e in clientEmails on n.client_id equals e.client_id into n_e
from ne in n_e.DefaultIfEmpty() //LEFT JOIN
join ph in clientPhones on n.client_id equals ph.client_id into n_p
from np in n_p.DefaultIfEmpty() //LEFT JOIN
select new PiiDiamondClient
{
ClientId = n.client_id,
DisplayName = n.display_name,
DisplayAddress = na.display_address,
EmailAddress = ne.email_address,
PhoneExtension = np.phone_ext,
PhoneNumber = np.phone_num
}).Distinct().ToListAsync();
return rows.OrderBy(x => x.ClientId).ToList();

Your last query can be rewritten in the following way. It will exactly get one record from JOIN which can multiply result set.
var query =
from n in clientNames
from a in clientAddresses.Where(a => n.client_id == a.client_id)
.Take(1).DefaultIfEmpty() //OUTER APPLY or LEFT JOIN on ROW_NUMBER query
from e in clientEmails.Where(e => n.client_id == e.client_id)
.Take(1).DefaultIfEmpty() //OUTER APPLY or LEFT JOIN on ROW_NUMBER query
from ph in clientPhones.Where(ph => n.client_id == ph.client_id)
.Take(1).DefaultIfEmpty() //OUTER APPLY or LEFT JOIN on ROW_NUMBER query
select new PiiDiamondClient
{
ClientId = n.client_id,
DisplayName = n.display_name,
DisplayAddress = a.display_address,
EmailAddress = e.email_address,
PhoneExtension = ph.phone_ext,
PhoneNumber = ph.phone_num
};

Related

How to get the Max from column - Entity Framework

I need get the Max from column using Entity Framework without use GroupBy.
var query= from r in table1
join rt in table2 on r.ID equals rt.ID
where rt.DateStart >= dtS && rt.DateEnd < dtE
select new datart { ID = rt.ID, Start = rt.DateStart, End = rt.DateEnd }
I want make something like this:
var query= from r in table1
join rt in table2 on r.ID equals rt.ID
where rt.DateStart >= dtS && rt.DateEnd < dtE
select new datart { ID = rt.ID, Start = Min.(rt.DateStart), End = Max.(rt.DateEnd) }
I don't understand your question, but I guess using OrderByDescending + First can help you:
var query= from r in table1
join rt in table2 on r.ID = r.rt.ID
where rt.DateStart >= dtS && rt.DateEnd < dtE
select new datart { ID = rt.ID, Start = rt.DateStart };
var result =
query
.OrderByDescending(t => t.DateStart)
.First();

How to do left join with group by in NET Core 3.0

As we know in .NET Core 3.0 a lot has changed and I'm building queries which don't run on client side memory.
I want to achieve a left join which count the items from child table keeping the parent intact if there no children against it.
Desired output query:
SELECT [c].[Type] AS [Name], COUNT([c0].Id) AS [Count]
FROM [CustomerTypes] AS [c]
LEFT JOIN [Customers] AS [c0] ON [c].[Id] = [c0].[CustomerTypeId]
WHERE [c0].[Id] IS NULL OR (([c0].[CompanyId] = 1) AND [c0].[CompanyId] IS NOT NULL)
GROUP BY [c].[Id], [c].[Type]
Entity Framework Queries
from types in _dbManager.CustomerTypes
join customers in _dbManager.Customers
on types.Id equals customers.CustomerTypeId into tempJoin
from leftJoined in tempJoin.DefaultIfEmpty()
where leftJoined == null || leftJoined.CompanyId == 1
group leftJoined by new { types.Id, types.Type } into grouped
select new TabCountBindingModel()
{
Name = grouped.Key.Type,
Count = grouped.Count(c => c != null)
}
Output Query
SELECT [c].[Type] AS [Name], COUNT(*) AS [Count]
FROM [CustomerTypes] AS [c]
LEFT JOIN [Customers] AS [c0] ON [c].[Id] = [c0].[CustomerTypeId]
WHERE [c0].[Id] IS NULL OR (([c0].[CompanyId] = 1) AND [c0].[CompanyId] IS NOT NULL)
GROUP BY [c].[Id], [c].[Type]
No matter what I try I always end up with query above, either this or an exception.
Tried these too
from types in _dbManager.CustomerTypes
join customers in _dbManager.Customers
on types.Id equals customers.CustomerTypeId into tempJoin
from leftJoined in tempJoin.DefaultIfEmpty()
where leftJoined == null || leftJoined.CompanyId == 1
group leftJoined by new { types.Id, types.Type } into grouped
select new TabCountBindingModel()
{
Name = grouped.Key.Type,
Count = grouped.Where(c=>c != null).Count()
}
Results in exception.

Convert nested T-SQL select query to linq

I have the following SQL query which I am trying to translate to a LINQ query:
SELECT
CustomerId,
SUM(Bills.BillAmount) AS BillAmountTotal,
SUM(COALESCE(Pay.Paid, 0)) AS [Payments]
FROM
Bills
LEFT OUTER JOIN
(SELECT
BillId,
SUM(PaymentAmount) AS [Paid]
FROM
BillPayments
GROUP BY
BillId) Pay ON Pay.BillId = Bills.Id
GROUP BY
CustomerId
I solved it using the following code :
var pay = from p in db.BillPayments
group p by new { p.BillId } into g
select new
{
payKey = g.Key,
TotalPayments = g.Sum(p => p.PaymentAmount)
};
var query = from c in db.Customers
join b in db.Bills on c.Id equals b.CustomerId
join p in pay on b.Id equals p.payKey.BillId into cs
from xx in cs.DefaultIfEmpty()
group new { c, b, xx } by new { c.Name, c.MobilePhone, c.Id, c.CustomerCode } into g
select new
{
Received = g.Key,
TotalPayment = g.Sum(p => p.xx.TotalPayments == null ? 0 : p.xx.TotalPayments),
TotlalBilling = g.Sum(p => p.b.BillAmount),
GrandTotal = g.Sum(p => p.b.BillAmount) - g.Sum(p => p.xx.TotalPayments == null ? 0 : p.xx.TotalPayments)
};

How to add a where clause to an EF query which has a grouping

I'm trying to find the number of bookings each user has made. This code works great - it does a join onto bookings and groups them and then does a count.
var bookingsByUser = from u in _repo.Users()
join b in _repo.Bookings() on u.UserId equals b.CreatedByUserId
into g
select new { UserId = u.UserId, TotalBookings = g.Count() };
However, I now want to exclude a certain restaurant, so I try to add a where clause:
var bookingsByUser = from u in _repo.Users()
join b in _repo.Bookings() on u.UserId equals b.CreatedByUserId
where b.RestaurantId != 21
into g
select new { UserId = u.UserId, TotalBookings = g.Count() };
However now I get an error "A query body must end with a select clause or a group clause". I can't seem to use "where" on the join table to filter the records before grouping. Is there a better way to do this?
Try this:
var bookingsByUser = from u in _repo.Users()
join b in _repo.Bookings() on u.UserId equals b.CreatedByUserId
where b.RestaurantId != 21
group b by u.UserId into sub
select new {
UserId = sub.Key,
TotalBookings = sub.Count()
};

LINQ to Entities: Convert SQL Sub Select

I got this figured out.
No need to answer.
The system says I have to wait 8 hours before answering my own questions. But for now the answer is below:
Here is the answer:
var startDate = DateTime.Today.AddDays(-30);
var results = (from h in Histories
join q in Quotes
on h.QuoteID equals q.QuoteID
join a in Agencies
on q.AgencyID equals a.AgencyID
where q.Status == "Inforce" &&
q.LOB == "Vacant" &&
q.EffectiveDate > startDate &&
h.Deleted == null &&
h.DeprecatedBy == null &&
h.TransactionStatus == "Committed" &&
a.DC_PLT_Roles.Any(r => r.Name == "Wholesaler")
group new {h} by new {h.PolicyNumber} into g
select new {
MaxHistoryID = g.Max (x => x.h.HistoryID),
comment = (from h2 in Histories
where h2.HistoryID == g.Max (x => x.h.HistoryID)
select h2.Comment).FirstOrDefault()
}).ToList();
The key code was:
comment = (from h2 in Histories
where h2.HistoryID == g.Max (x => x.h.HistoryID)
select h2.Comment).FirstOrDefault()
We are in the process of converting SQL / Stored Procedures to LINQ to Entities statements. And I can’t figure out the proper syntax for a sub select.
Currently I am converting this SQL:
declare #startDate DateTime
set #startDate = DATEADD(DD, -30, GETDATE())
select * from history where historyid in(
select MAX(h.historyid) as HistoryId
from History h (nolock)
inner join Quote q (nolock) on h.QuoteID = q.QuoteID
inner join Agency (nolock) a on q.AgencyID = a.AgencyID
inner join DC_PLT_EntityRoles er (nolock) on a.AgencyID = er.EntityID
inner join DC_PLT_Roles (nolock) r on er.RoleID = r.RoleID
where
q.Status = 'Inforce'
and q.LOB = 'Vacant'
and q.EffectiveDate > #startDate
and h.Deleted is null --
and h.DeprecatedBy is null --
and h.TransactionStatus = 'Committed'
and r.Name = 'Wholesaler'
group by h.PolicyNumber)
As you can see the code above is made up of two select statements. The main select (select * from history).. And a filter select (select MAX(h.historyid)…)
I got the filter select working (See below):
var startDate = DateTime.Today.AddDays(-30);
var results = (from h in Histories
join q in Quotes
on h.QuoteID equals q.QuoteID
join a in Agencies
on q.AgencyID equals a.AgencyID
where q.Status == "Inforce" &&
q.LOB == "Vacant" &&
q.EffectiveDate > startDate &&
h.Deleted == null &&
h.DeprecatedBy == null &&
h.TransactionStatus == "Committed" &&
a.DC_PLT_Roles.Any(r => r.Name == "Wholesaler")
group new {h} by new {h.PolicyNumber} into g
select new {
MaxHistoryID = g.Max (x => x.h.HistoryID)
}).ToList();
However I can’t figure out the proper syntax to set up the main select. (Basically getting the records from the History table using the HistoryID from the filter select.)
Any help would be appreciated.
Thanks for your help.
I figured it out, here is the code:
var startDate = DateTime.Today.AddDays(-30);
var results = (from h in Histories
.Include("Quote")
.Include("Quote.Agency")
where h.Quote.Status == "Inforce" &&
h.Quote.LOB == "Vacant" &&
h.Quote.EffectiveDate > startDate &&
h.Deleted == null &&
h.DeprecatedBy == null &&
h.TransactionStatus == "Committed" &&
h.Quote.Agency.DC_PLT_Roles.Any(r => r.Name == "Wholesaler")
group new {h} by new {h.PolicyNumber} into g
select new {
XMLData = (from h2 in Histories
where h2.HistoryID == g.Max (x => x.h.HistoryID)
select h2.XMLData).FirstOrDefault()
}).ToList();
The key logic is:
select new {
XMLData = (from h2 in Histories
where h2.HistoryID == g.Max (x => x.h.HistoryID)
select h2.XMLData).FirstOrDefault()
}).ToList();
Gotta love the Nested Query