Entity Framework Query select hard-coded user created column - linq

I'm creating a select from multiple tables using a union as I need to return a list of activities that has occurred for a particular client on the database. I need to return each union with an added column so I can tell the difference between the results. If I was to do the query in SQL it would look something like this:
SELECT cn.NoteID, cn.Note, cn.InsertedDate, 'Note Added' Notes
FROM Client c
INNER JOIN ClientNotes cn ON cn.ClientID = c.ID
WHERE c.ClientID = #ClientID
UNION
SELECT rc.ID, rc.CommNote, rc.InsertedDate, 'Communication Added' Notes
FROM ReceivedCommunication rc
LEFT JOIN Job j ON j.ID = rc.JobID
WHERE j.ClientID = #ClientID or rc.ClientID = #ClientID
My Question is how in Entity Framework using IQuerable do I return the hard-coded Notes column?
I have something like this so far:
Dim client as IQueryable(Of myresultclass) =
(From c As Client
Join cn As ClientNotes In ClientCompanyNotes On c.ID Equals cn.ClientID
Where c.ClientID = ClientID
Select cn.NoteID, cn.Note, cn.InsertedDate).Union(
From rc As ReceivedCommunication In ReceivedCommunications
Join j As Job In Jobs On j.ID Equals rc.JobID
Where j.ClientID = ClientID or rc.ClientID = ClientID
Select rc.ID, rc.CommNote, rc.InsertedDate)
Thanks for your help

Ok worked it out, should have been obvious. For anyone with the same issue, I had to update my Select from Select cn.NoteID, cn.Note, cn.InsertedDate to:
Select New myresultclass With {
.ActivityID = cn.NoteID,
.ActivityType = "Note Added"
.InsertedDate = cn.InsertedDate
}
for each one of the unions that I had
Thanks

Related

EF linq query working in linqpad but not in the App

I have creatred a query in linqpad and it works but when i try to use it in my blazor app the app throws an error.
I have three tables, tblOpportunity, tblOppStatus and lkpStatus
and an opportunity can have many status's depending on how mature the enquiry is. So i need to get all the latestest status for each opportunity. I also want to be able to filter the dataset so i get only opportunities with staus of x, y and z
void Main()
{
var output=from o in TblOpportunities
join sub in (
//get the latest status ID for the from the joining table
from smax in TblOppStatuses
group smax by smax.OpportunityID
into g
select new
{
OName = g.Key,
MaxS = (from t2 in g select t2.OppStatusID).Max()
})
on o.OpportunityID equals sub.OName
join st in TblOppStatuses on sub.MaxS equals st.OppStatusID
//get the statu sname form the lookup table
join lst in Lkp_Statuses on st.StatusID equals lst.StatusID
join c in TblClients on o.ClientID equals c.ClientID
select new
{
c.ClientName,
o.OpportunityName,
sub.MaxS,
lst.Status,
lst.StatusID
};
int[] filter = { -1, 62};
output.Where(of=>filter.Contains(of.StatusID))
.Dump();
}
The error that comes in the subquery
MaxS = (from t2 in g select t2.OppStatusID).Max()
if i comment this out the query runs but doesn't give me what i want. Can anyone advise what i am doing wrong As I say it works fine in Linqpad.

How to select different fields using .Concat for Union All query

I’m doing some preliminary work getting data consumption ready from a webAPI controller in MVC. I’m using a linq query that mimics a union query that uses two joins. However, I need the select statement to ask for a different field on each side of the union. This works fine in SSMS when I run the query, but when I create the query in linq it will not allow the fields to be different. It gives an error saying that each anonymous type requires a receiver of the same type. This is referring to the two anonymous types created via select new { }… Here’s the two queries.
In SQL:
Select m.Last_Name, first_name, m.dc_number, Dept_Job as JobOrStatus FROM
Master_Roster m
INNER JOIN Class_Assignment a on m.dc_number = a.dc_number
where a.subject_am = 'y1'
AND Start_Date_AM <= '1/31/18'
UNION ALL
Select m.Last_Name, first_name, m.dc_number, status_am FROM Master_Roster m
INNER JOIN Attend_am_y1 at on m.dc_number = at.dc_number
where at.class_date_am >= '1/1/18'
AND at.class_date_am <= '1/31/18'
ORDER BY Last_Name
In linq:
(from m in db.Master_Roster
join c in db.Class_Assignment
on m.dc_number equals c.dc_number
orderby m.Last_Name
where c.Subject_AM == "y1"
select new { m.dc_number, m.Last_Name, m.First_Name }).Concat(
from m in db.Master_Roster
join a in db.attend_am_y1 on m.dc_number equals a.dc_number
orderby m.Last_Name
where a.class_date_am >= date1 &&
a.class_date_am <= date2
select new { m.dc_number, m.Last_Name, m.First_Name });
If I were to add dept_job and status_am to the linq query, it throws aforementioned error. I’d like to do this without using a stored procedure. Any ideas?
Instead of putting into 2 anonymous types, put into the same viewmodel and concat. Eg:
Internal Class ConcatViewModel{
public int DcNumber {get;set;}
public string LastName {get;set;)
// etc...
}
Then:
select new ConcatViewModel{
DcNumber = m.dc_number,
LastName = m.Last_Name,
FirstName = m.First_Name,
Status = m.Dept_Job
}
and
select new ConcatViewModel{
DcNumber = m.dc_number,
LastName = m.Last_Name,
FirstName = m.First_Name,
Status = m.status_am
}
(assuming that status_am and Dept_Job are the same underlying type)

Linq left outer group by, then left outer the group

I've this query that i'm trying to put as linq:
select *
from stuff
inner join stuffowner so on so.stuffID = stuff.stuffID
left outer join (select min(loanId) as loanId, stuffownerId from loan
where userid = 1 and status <> 2 group by stuffownerId) t on t.stuffownerid = so.stuffownerid
left outer join loan on t.LoanId = loan.LoanId
when this is done, I would like to do a linq Group by to have Stuff has key, then stuffowners + Loan as value.
I can't seem to get to a nice query without sub query (hence the double left outer).
So basically what my query does, is for each stuff I've in my database, bring the owners, and then i want to bring the first loan a user has made on that stuff.
I've tried various linq:
from stuff in Stuffs
join so in StuffOwners on stuff.StuffId equals so.StuffId
join tLoan in Loans on so.StuffOwnerId equals tLoan.StuffOwnerId into tmpJoin
from tTmpJoin in tmpJoin.DefaultIfEmpty()
group tTmpJoin by new {stuff} into grouped
select new {grouped, fluk = (int?)grouped.Max(w=> w.Status )}
This is not good because if I don't get stuff owner and on top of that it seems to generate a lot of queries (LinqPad)
from stuff in Stuffs
join so in StuffOwners on stuff.StuffId equals so.StuffId
join tmpLoan in
(from tLoan in Loans group tLoan by tLoan.StuffOwnerId into g
select new {StuffOwnerId = g.Key, loanid = (from t2 in g select t2.LoanId).Max()})
on so.StuffOwnerId equals tmpLoan.StuffOwnerId
into tmptmp from tMaxLoan in tmptmp.DefaultIfEmpty()
select new {stuff, so, tmptmp}
Seems to generate a lot of subqueries as well.
I've tried the let keyworkd with:
from tstuffOwner in StuffOwners
let tloan = Loans.Where(p2 => tstuffOwner.StuffOwnerId == p2.StuffOwnerId).FirstOrDefault()
select new { qsdq = tstuffOwner, qsdsq= (int?) tloan.Status, kwk= (int?) tloan.UserId, kiwk= tloan.ReturnDate }
but the more info i get from tLoan, the longer the query gets with more subqueries
What would be the best way to achieve this?
Thanks

Left Outer Join works in LINQPad (connected using EF 4.3) but fails in application

I have an application that successfully utilizes LINQ to perform LEFT OUTER JOINs several instances; however, in one case it fails to work as expected.
Testing in LINQPad (using LINQ-to_SQL) produced the correct result; however, to be sure I changed to the LINQPad beta version 4.42.05 and successfully connected using my application's DLL and the connectionString from its web.config file (as per the Add Connection dialog). Again, LINQPad is successful in returning the proper result and clearly generates the expected left outer join in TSQL but the same code in the application fails.
In debugging the function, I get the "Object reference not set to an instance of an object." error. Please see additional descriptions after the following code and related TSQL. Note the relationship involves customer who have one-or-more stores and whoes stores have zero-or-more departments. Therefore, some returned records will not have a department (hence the requirement for the left outer join).
The following code works perfectly in LINQPad:
var model = (from h in SalesOrderHeaders
join c in Customers on h.CustomerId equals c.CustomerId
join s in Stores on h.StoreId equals s.StoreId
join d in Departments on h.DepartmentId equals d.DepartmentId into outer
from o in outer.DefaultIfEmpty()
select new
{
OrderId = h.SalesOrderHeaderId,
OrderDetailId = 1,
SalesOrderDate = h.SalesOrderDate,
DeliveryDateTime = h.DeliveryDateTime,
Customer = c.Customer,
Store = s.Store,
Department = (o.Department == null) ? "None" : o.Department,
FullDescription = "None",
Qty = 0,
UoM = "None",
}).OrderBy (m => m.OrderId);
When the code below is used in the application it fails:
var model = from h in headers
join c in customers on h.CustomerId equals c.CustomerId
join s in stores on h.StoreId equals s.StoreId
join d in departments on h.DepartmentId equals d.DepartmentId into outer
from o in outer.DefaultIfEmpty()
select new SalesOrderGridViewModel
{
OrderId = h.SalesOrderHeaderId,
OrderDetailId = 1,
SalesOrderDate = h.SalesOrderDate,
DeliveryDateTime = h.DeliveryDateTime,
Customer = c.Name,
Store = s.Name,
Department = (o.Name == null) ? "None" : o.Name,
FullDescription = "None",
Qty = 0,
UoM = "None",
};
However, when I change the code in the application so that the boolean in the assignment of the department field of the result references the join element from the headers variable (h.DepartmentId == null) as in the following code:
var model = from h in headers
join c in customers on h.CustomerId equals c.CustomerId
join s in stores on h.StoreId equals s.StoreId
join d in departments on h.DepartmentId equals d.DepartmentId into outer
from o in outer.DefaultIfEmpty()
select new SalesOrderGridViewModel
{
OrderId = h.SalesOrderHeaderId,
OrderDetailId = 1,
SalesOrderDate = h.SalesOrderDate,
DeliveryDateTime = h.DeliveryDateTime,
Customer = c.Name,
Store = s.Name,
Department = (h.DepartmentId == null) ? "None" : o.Name,
FullDescription = "None",
Qty = 0,
UoM = "None",
};
The expected result is returned.
Interestingly the subtle difference in the TSQL generated first from the original code:
SELECT [t4].[SalesOrderHeaderId] AS [OrderId], [t4].[SalesOrderDate],
[t4].[DeliveryDateTime], [t4].[Customer], [t4].[Store],
[t4].[value] AS [Department]
FROM (
SELECT [t0].[SalesOrderHeaderId], [t0].[SalesOrderDate],
[t0].[DeliveryDateTime], [t1].[Customer], [t2].[Store],
(CASE
WHEN [t3].[Department] IS NOT NULL THEN [t3].[Department]
ELSE CONVERT(NVarChar(50),#p0)
END) AS [value]
FROM [SalesOrderHeaders] AS [t0]
INNER JOIN [Customers] AS [t1] ON [t0].[CustomerId] = [t1].[CustomerId]
INNER JOIN [Stores] AS [t2] ON [t0].[StoreId] = ([t2].[StoreId])
LEFT OUTER JOIN [Departments] AS [t3]
ON [t0].[DepartmentId] = ([t3].[DepartmentId])) AS [t4]
ORDER BY [t4].[SalesOrderHeaderId]
And here from the revised code, where the boolean was changed to test the value of the DepartmentId in the original headers table ([t3].[Department] versus [t0].[DepartmentId] ), appears to be the solution:
SELECT [t4].[SalesOrderHeaderId] AS [OrderId], [t4].[SalesOrderDate],
[t4].[DeliveryDateTime], [t4].[Customer], [t4].[Store],
[t4].[value] AS [Department]
FROM (
SELECT [t0].[SalesOrderHeaderId], [t0].[SalesOrderDate],
[t0].[DeliveryDateTime], [t1].[Customer], [t2].[Store],
(CASE
WHEN [t0].[DepartmentId] IS NOT NULL THEN [t3].[Department]
ELSE CONVERT(NVarChar(50),#p0)
END) AS [value]
FROM [SalesOrderHeaders] AS [t0]
INNER JOIN [Customers] AS [t1] ON [t0].[CustomerId] = [t1].[CustomerId]
INNER JOIN [Stores] AS [t2] ON [t0].[StoreId] = ([t2].[StoreId])
LEFT OUTER JOIN [Departments] AS [t3]
ON [t0].[DepartmentId] = ([t3].[DepartmentId])) AS [t4]
ORDER BY [t4].[SalesOrderHeaderId]
While I have found a way to make this work; because it works both ways in LINQPad and successfully in numerous other LINQ queries scattered throughout my application, its failure in the original form in this one location gives me concern.
Ultimately, it appears to fail in the application when I test the returned value of the left outer join. However, this is the documented practice in many books and articles. So my final question is does anyone have any insight into why this would occur and/or how it can work in LINQPad (using the applications DLL and against the same DB)?
This is a classic example of how not to write LINQ queries - thinking in SQL and then transliterating into LINQ.
With LINQ, you can avoid the joins completely and formulate your query as follows:
from h in SalesOrderHeaders
orderby h.OrderId
select new
{
OrderId = h.SalesOrderHeaderId,
OrderDetailId = 1,
SalesOrderDate = h.SalesOrderDate,
DeliveryDateTime = h.DeliveryDateTime,
c.Customer.Customer,
s.Store.Store,
Department = (h.Department == null) ? "None" : h.Department.Department,
FullDescription = "None",
Qty = 0,
UoM = "None"
}
It appears that [t0].DepartmentID (from SalesOrderHeaders) can be null. Your LEFT OUTER JOIN relies on this value. This ends up comparing [t3].DepartmentID to null (in some cases).
You might need to use your CASE statement from the select in the JOIN as well.

Join statement in Linq to Sql

I need to write Join statment after writing query in linq
example :
var Query = (from Tab in Db.Employees
select Tab)
as i have some cases to perform join operation so
i need to do it on this Query Query.Join(Join with another Table like Department); I need the Syntax
if (DeptID != -1){ Query.Join(Join with table Department where FkDeptID = DeptID); }
Consider the usage of join in the LINQ 'query syntax':
from t1 in db.Table1
join t2 in db.Table2 on t1.field equals t2.field
select new { t1.field2, t2.field3}
Something like this?
var results = (from q in Query
join m in myList on q.SomeID = m.SomeID
select unknown);
Try using this query:
var Query =
from e in Db.Employees
join d in Db.Departments on e.FkDeptID equals d.DeptID into departments
select new
{
Employee = e,
Department = departments.SingleOrDefault(),
};
This works assuming that when e.FkDeptID == -1 that there is no record in the Departments table and in that case Department would be assigned null.
You should never have more than one department for an employee so I've used SingleOrDefault rather than FirstOrDefault.

Resources