I working on an app with a SQLite database, now I want to select information from multiple tables using linq, here it is what I did:
from c in dataContext.Convention
join e in dataContext.Engineer on c.Engineer equals e.Code
join o in dataContext.Owner on c.Owner equals o.Code
join t in dataContext.ProjectType on c.ProjectType equals t.Id
join cs in dataContext.ConventionState on c.State equals cs.Id
join sc in dataContext.SiteControl on c.Code equals sc.CodeCv
join pc in dataContext.PlanControl on c.Code equals pc.CodeCv
join scs in dataContext.SiteState on sc.State equals scs.Id
join pcs in dataContext.PlanState on pc.State equals pcs.Id
join rcs in dataContext.ReceptionState on sc.Reception equals rcs.Id
join b in dataContext.Bill on c.Code equals b.CodeCv
where cs.Abr == "EC"
group new { c, e, o, t, scs, pcs, rcs, b } by new
{ c.Code, c.NumSeq, c.Year, c.TotalAmount, c.Title, e.LastName, e.FirstName, o.Name,
tprjt = t.Abr,
scState = scs.Abr,
pcState = pcs.Abr,
rcState = rcs.Abr
} into cvgrp
select new
{
Code = cvgrp.Key.Code,
N_Seq = cvgrp.Key.NumSeq,
Exercice = cvgrp.Key.Year,
Intitulé = cvgrp.Key.Title,
Ingenieur = cvgrp.Key.LastName + " " + cvgrp.Key.FirstName,
MaitreOuvrage = cvgrp.Key.Name,
TypeProjet = cvgrp.Key.tprjt,
CtrlPlan = cvgrp.Key.pcState,
CtrlChantier = cvgrp.Key.scState,
Reception = cvgrp.Key.rcState,
Montant = cvgrp.Key.TotalAmount,
MontantFacturé = cvgrp.Sum(x => x.b.Amount),
MontantRestant = cvgrp.Key.TotalAmount - cvgrp.Sum(x => x.b.Amount),
MontantCréance = cvgrp.Sum(x => x.b.IsPaid == "False" ? x.b.Amount : 0.0 )
}
It seems working but I really don't understand clearly what I did especially in the group by clause. If I add an into clause after the joins before the group by the identifier will work as a table with all the tables joined ?.
I need some explanation to understand linq more.
Thank you in advance.
Related
I have the following LINQ-to-Entities query for MySQL DB
var data = (from agent in db.User
join agentrole in db.UserRole.DefaultIfEmpty() on agent.Id equals agentrole.UserId
join role in db.Role.DefaultIfEmpty() on agentrole.RoleId equals role.Id
join department in db.Department.DefaultIfEmpty() on role.DepartmentId equals department.Id
join client in db.Client.DefaultIfEmpty() on agent.Id equals client.AssignedUserId
join aggclient in db.AggClient.DefaultIfEmpty() on client.Id equals aggclient.ClientId
group new { agent, department, aggclient} by agent.Id into grp
select new
{
grp.Key,
agentName = grp.Max(a => a.agent.FirstName + " " + a.agent.LastName),
departmentNames = "",
newDepositorsCount = 0,
FTDSum = grp.Sum(a => a.aggclient.FirstDepositAmountEuro),
depcount =grp.Count(a => a.department != null),
aggclientfilter = grp.Where(a => a.aggclient != null && a.aggclient.FirstDepositAmount>0).Sum(a => a.aggclient.FirstDepositAmount)
});
On the current query, the last two operations are not working.
The entity cannot parse count and where operations.
change select clause to :
select new
{
grp.Key,
agentName = grp.agent.Max(a => a.FirstName + " " + a.LastName),
departmentNames = "",
newDepositorsCount = 0,
FTDSum = grp.aggclient.Sum(a => a.FirstDepositAmountEuro),
depcount = grp.department.Count(),
aggclientfilter = grp.aggclient.Where(a => a.FirstDepositAmount>0).Sum(a => a.FirstDepositAmount)
});
I assume that you do not use EF Core 5.x, because it supports filtered count.
Problem that there is no correct translation to SQL of such LINQ query. But there are tricks which can return needed result. Also corrected bad LEFT join.
var data =
from agent in db.User
join agentrole in db.UserRole on agent.Id equals agentrole.UserId into ga
from agentrole in ga.DefaultIfEmpty()
join role in db.Role on agentrole.RoleId equals role.Id into gr
from role in gr.DefaultIfEmpty()
join department in db.Department on role.DepartmentId equals department.Id into dg
from department in dg.DefaultIfEmpty()
join client in db.Client on agent.Id equals client.AssignedUserId
join aggclient in db.AggClient on client.Id equals aggclient.ClientId into acg
from aggclient in acg.DefaultIfEmpty()
group new { agent, department, aggclient} by agent.Id into grp
select new
{
grp.Key,
agentName = grp.Max(a => a.agent.FirstName + " " + a.agent.LastName),
departmentNames = "",
newDepositorsCount = 0,
FTDSum = grp.Sum(a => a.aggclient.FirstDepositAmountEuro),
depcount = grp.Sum(a => a.department != null ? 1 : 0),
aggclientfilter = grp.Sum(a => a.aggclient.FirstDepositAmount > 0 ? a.aggclient.FirstDepositAmount : 0)
};
I have the following query in SQL which returns 5 rows of data:
SELECT DISTINCT c.Id, c.FirstName, c.LastName, c.PhoneNumber, 'Waiting to be sent'
FROM DistributionGroupMembers dgm
JOIN Contacts c on dgm.ContactId = c.Id
JOIN DistributionGroups dg on dgm.DistributionGroupId = dg.Id
WHERE dg.Id IN (
SELECT DistributionGroupId
FROM DistributionGroupInSms
WHERE SmsId = 40
)
When I try to run the adequate query in C# using LINQ it won't return anything:
int[] groupIDs = await _db.DistributionGroupInSms.Where(dgis => dgis.SmsId == message.Id).Select(g => g.Id).ToArrayAsync();
var recipients = await (from dgm in _db.DistributionGroupMembers
join c in _db.Contacts on dgm.ContactId equals c.Id
join dg in _db.DistributionGroups on dgm.DistributionGroupId equals dg.Id
where groupIDs.Contains(dg.Id)
select new
{
ID = c.Id,
FN = c.FirstName,
LN = c.LastName,
PN = c.PhoneNumber,
SR = "Waiting to be sent"
}).Distinct().ToListAsync();
What am I doing wrong?
Can you simply do a join:
int[] groupIDs = await _db.DistributionGroupInSms.Where(dgis => dgis.SmsId == message.Id).Select(g => g.Id).ToArrayAsync();
var recipients = await (from dgm in _db.DistributionGroupMembers
join c in _db.Contacts on dgm.ContactId equals c.Id
join dg in _db.DistributionGroups on dgm.DistributionGroupId equals dg.Id
join gIds in groupIDs on gIds equals dg.Id
select new
{
ID = c.Id,
FN = c.FirstName,
LN = c.LastName,
PN = c.PhoneNumber,
SR = "Waiting to be sent"
}).Distinct().ToListAsync();
I figured it out, in the select clause by getting the groupIDs I selected Id instead of another field in the table called DistributionGroupId. Thanks everyone for the input
using the northwind DB, i have to make a query to get employeename, amount of orders per employee and average price of those orders
this is what the query looks like in SQL
SELECT TOP 10
a.LastName, a.FirstName, amountOfOrders = COUNT(DISTINCT b.OrderID), AveragePricePerOrder = SUM(c.Quantity*c.UnitPrice) /COUNT(DISTINCT b.OrderID)
FROM Employees a join orders b on (a.EmployeeID = b.EmployeeID)
join [Order Details] c on b.OrderID = c.OrderID
Group BY a.EmployeeID, a.LastName, a.FirstName
ORDER BY amountOfOrders Desc
this runs fine but I have to make this in c# and I am a little stuck
So far, I have got this
var query_rx = (from c in ctx.Employees
join or in ctx.Orders on c.EmployeeID equals or.EmployeeID
join ord in ctx.Order_Details on or.OrderID equals ord.OrderID
group c by new
{
c.EmployeeID,
c.LastName,
c.FirstName,
amount = c.Orders.Count
} into c
orderby c.Key.amount descending
select new
{
c.Key.LastName,
c.Key.FirstName,
amountOfOrders = c.Key.amount
}).Take(10);
"edit" I am having trouble working the average in, tried a lot of things but I can't get it to work
"edit" I have changed the query a bit with help from Dohnal's suggestion.
This looks almost exactly like what i want in terms of columns, except that the field lastname and firstname are blank, even with ToString
var query_rx = (from or in ctx.Order_Details
join ord in ctx.Orders on or.OrderID equals ord.OrderID
group or by new
{
ord.EmployeeID
} into c
orderby c.Select(x => x.OrderID).Distinct().Count() descending
select new
{
Lastname = (from emp in ctx.Employees
where c.Key.EmployeeID == emp.EmployeeID
select emp.LastName),
Firstname = (from emp in ctx.Employees
where c.Key.EmployeeID == emp.EmployeeID
select emp.FirstName),
c.Key.EmployeeID,
AmountOfOrders = c.Select(x => x.OrderID).Distinct().Count(),
AveragePricePerOrder = c.Sum(x => x.Quantity * x.UnitPrice) / c.Select(x => x.OrderID).Distinct().Count()
}).Take(10);
Try this query:
var query = (from emp in ctx.Employers
join order in ctx.Orders on emp.EmployeeID equals order.EmployerID
join orderDet in ctx.Order_Details on order.OrderID equals orderDet.OrderID
group new { emp, order, orderDet }
by new { emp.FirstName, emp.LastName, emp.EmployeeID, order.OrderID }
into orderGroup
let a = new
{
orderGroup.Key.EmployeeID,
orderGroup.Key.FirstName,
orderGroup.Key.LastName,
orderGroup.Key.OrderID,
sum1 = orderGroup.Sum(x => x.orderDet.Quantity * x.orderDet.UnitPrice),
}
group a by new { a.FirstName, a.LastName, a.EmployeeID } into empGroup
let a2 = new
{
empGroup.Key.FirstName,
empGroup.Key.LastName,
sum = empGroup.Sum(x => x.sum1),
count = empGroup.Count()
}
orderby a2.count descending
select new
{
a2.FirstName,
a2.LastName,
amountOfOrders = a2.count,
AveragePricePerOrder = a2.sum / a2.count
}).Take(10);
I have a query in entity framework which gets some messages from tables.
var ur = (from m in en.Messages
join mb in en.aspnet_Membership on m.FromUserId equals mb.UserId
join urs in en.UserProfiles on mb.UserId equals urs.UserId
join g in en.Groups on m.ToUserId equals g.GroupId
join ug in en.UserInGroups on g.GroupId equals ug.GroupId
where ug.UserId == userId
select new
{
InboxId = m.MessageId,
FromUser = urs.RaveName.Trim(),
CreatedOn = m.CreatedOn
}
).Concat(
// msg is not deleted
from m in en.Messages
join mb in en.aspnet_Membership on m.FromUserId equals mb.UserId
join urs in en.UserProfiles on mb.UserId equals urs.UserId
where m.ToUserId == userId
select new
{
InboxId = m.MessageId
,
FromUser = urs.RaveName.Trim()
,
CreatedOn = m.CreatedOn
}
);
I have another table which shows whether the message is used:
var msg = (from m in en.MessagesUsed
where m.UserId == userId
select m
);
Now, I need to check: Is there a message in 'ur' that is not in 'msg'? In T-SQL, we can use:
SELECT 1
FROM ur
WHERE NOT EXISTS (SELECT 1 FROM msg WHERE msg.Id = ur.InboxId AND msg.FromUser = ur.FromUser AND msg.CreatedOn = ur.CreatedOn)
to check this. But how to do it in LINQ?
Thanks
I am needing to make a left join and also use the select operator case.
My LINQ basic is this and works:
resultado.Dados =
(
from a in db.AgendaHorario
join b in db.Agenda on a.AgendaID equals b.AgendaID
join c in db.Profissional on a.ProfissionalID equals c.ProfissionalID into d
from e in d.DefaultIfEmpty()
select new
{
id = a.AgendaHorarioID,
Medico = e.Identificacao
});
But I must add a new field and it should be formatted, then my LINQ looked like this:
resultado.Dados =
(
from a in db.AgendaHorario
join b in db.Agenda on a.AgendaID equals b.AgendaID
join c in db.Profissional on a.ProfissionalID equals c.ProfissionalID into d
from e in d.DefaultIfEmpty()
select new
{
id = a.AgendaHorarioID,
Medico = e.Identificacao,
start = a.Horario.ToString("yyyy-MM-dd HH:mm:ss")
}
);
This error happens:
LINQ to Entities does not recognize the method 'System.String ToString(System.String)' method, and this method cannot be translated into a store expression.
If you add the ToList() or AsEnumerable() in db.AgendaHorario.ToList() and db.Agenda.ToList() and db.Profissional.ToList() error that appears is:
Object reference not set to an instance of an object.
What should I do to have a left join with case and fields and formatted fields
Try this:
resultado.Dados =
(
from a in db.AgendaHorario
join b in db.Agenda on a.AgendaID equals b.AgendaID
join c in db.Profissional on a.ProfissionalID equals c.ProfissionalID into d
from e in d.DefaultIfEmpty()
select new
{
id = a.AgendaHorarioID,
Medico = e.Identificacao,
start = a.Horario
}).AsEnumerable().Select(x => new
{
id = x.id,
Medico = x.Medico,
start = x.start.ToString("yyyy-MM-dd HH:mm:ss")
}
);
try to set the string in a variable then assign it to your query like this:
var myValue = Horario.ToString("yyyy-MM-dd HH:mm:ss");
resultado.Dados = (
from a in db.AgendaHorario
join b in db.Agenda on a.AgendaID equals b.AgendaID
join c in db.Profissional on a.ProfissionalID equals c.ProfissionalID into d
from e in d.DefaultIfEmpty()
select new
{
id = a.AgendaHorarioID,
Medico = e.Identificacao,
start = myValue
} );