Entity Framework Joining multiple tables and display multiple data sets - c#

this is my code to fetch data from multiple table and return it as json. However it only allow one to one relationship.
Problem: DoctorNote have multiple result sets, i am having problem fetching the data. Error"Sequence contains more than one element". Any suggestion on how i can fetch the many relationship results?
var person = (from p in _context.Patients
join e in _context.PatientAllocations
on p.patientID equals e.patientID
join d in _context.DoctorNotes
on p.patientID equals d.patientID
where p.patientID == patientID
select new
{
patient_patientID = p.patientID,
patient_isDeleted = p.isDeleted,
patient_isApproved = p.isApproved,
patient_updateBit = p.updateBit,
patientallocation_caregiverID = e.caregiverID,
patientallocation_doctorID = e.doctorID,
DoctorNote_doctorNoteID = d.doctorNoteID,
DoctorNote_note = d.note,
DoctorNote_createDateTime = d.createDateTime,
DoctorNote_patientID = d.patientID,
DoctorNote_isApproved = d.isApproved,
DoctorNote_isDeleted = d.isDeleted,
}).ToList().SingleOrDefault();
return Ok(person);

Exception is thrown by SingleOrDefault method, so based on that fact it seems your query returns more than one element.
To get collection of DoctorNotes, just remove SingleOrDefault method from your query

You are using SingleOrDefault() which assume that your query will return at most one record. You may try FirstOrDefault() it assume query can return any number of record and you want first.
You may see this to understand difference between the two
| 0 values | 1 value | > 1 value
FirstOrDefault | Default | First value | First value
SingleOrDefault | Default | First value | Exception

The query still returns 3 DocterNotes.
The FirstOrDefault is on the total query, not on the DocterNotes.
Can you add a where clause?
For example, this should work:
where p.patientID == patientID
and DoctorNote_doctorNoteID == 3

Related

Take any single record if matching records not available - Linq

I have linq query which has multiple records & I am filtering these records using a where clause.
Now, if the filtered records return nothing then I need to make it default to take default any single record from list.
var resultStaffGua = (from s in _db.Students
join sg in _db.StudentStaffGuardians on s.StudentID equals sg.StudentId
join g in _db.Staffs on sg.StaffId equals g.StaffID
join lr in _db.luRelationTypes on sg.RelationTypeId equals lr.RelationTypeID
join ga in _db.StaffAddresses on g.StaffID equals ga.StaffID
join ad in _db.Addresses on ga.AddressID equals ad.AddressID
where
lse.StatusID == (int?)Extension.StatusType.Active
&& lse.TenantID == tenantid
select new
{
g.FirstName,
g.LastName,
IsPrimary = sg.IsPrimaryGuardian,
se.Email,
Phone = sphon.PhoneNumber,
lr.RelationCD,
gdnr.GenderCD,
ad.Zipcode
}).Where(i=>i.IsPrimary==true);
if resultStaffGua count 0, I need one record from resultStaffGua. Thank you
if result count 0, I need one record from parentList.
Sometimes the obvious solution is the best. Why not add this after your code?
if (resultStaffGua.Count() == 0)
{
resultStaffGua = parentList.First();
}
If you want to be "clever" and do it all in one line (and I guess it would save a DB transaction too possibly) you could exchange your Where for an OrderBy and a Take.
So instead of:
).Where(i=>i.IsPrimary==true);
You could do:
).OrderBy( i => i.IsPrimary ? 0 : 1 ).Take(1);
This will prioritize any record that has an IsPrimary set to true, but it'll get one record regardless of whether any match.
Assuming that your intent is to retrieve one single record (there's at most one record with IsPrimary==true):
var query = (from s in...); //The whole query except the "where"
var resultStaffGua = query.SingleOrDefault(i=>i.IsPrimary==true) ?? query.First();
Otherwise, if the query could actually return more than one result:
var query = (from s in...);
var resultStaffGua = query.Where(i=>i.IsPrimary==true);
if(resultStaffGua.Count() == 0) resultStaffGua = new[] { query.First(); }

How do I do this SQL in LINQ

I have something I can do really easily in SQL but I just can't figure out how to do it in LINQ. So I have 3 tables: Return, ReturnItem, and ReturnItemTest. Return has 1..n ReturnItems and ReturnItem has 0..1 ReturnItemTests. The tables look like this:
Return
======
ReturnId int not null (PK)
ReturnName nvarchar(max) not null
ReturnItem
==========
ReturnItemId int not null (PK)
ReturnId int not null (FK)
ReturnItemStatus int not null
ReturnItemTest
==============
ReturnItemId int not null (PK, FK)
ReturnItemTestStatus int not null
Each return has return items, and each return item may have 0 or 1 tests. Both return items and return item tests have a status. I want to count up how many return item status codes and return item test status codes there are, grouping by the status number of both. However a LEFT OUTER JOIN is needed because a return item may not have a test. So in SQL I say:
SELECT
ri.[ReturnItemStatus] AS ItemStatus,
rit.[ReturnItemTestStatus] AS TestStatus,
COUNT([ReturnItem].[ReturnItemStatus]) as ComboCount
FROM
[Return] r
INNER JOIN [ReturnItem] ri ON r.ReturnId = ri.ReturnId
LEFT OUTER JOIN [ReturnItemTest] rit ON ri.ReturnItemId = rit.ReturnItemId
GROUP BY
ri.[ReturnItemStatus], rit.[ReturnItemTestStatus]
This gives me a result showing all the extant combinations of return item status, return item test status, and the count for each combination. How do I achieve the same with LINQ? I got this far:
var returns =
(
from r in ctx.Returns
join ri in ctx.ReturnItems on r.ReturnID equals ri.ReturnID
join rit in ctx.ReturnItemTests on ri.ReturnItemID equals rit.ReturnItemTestID into ritJoined
from rit in ritJoined.DefaultIfEmpty()
select new {
ReturnItemStatus = ri.ReturnItemStatus,
ReturnItemTestStatus = rit == null ? null : (int?)rit.ReturnItemTestStatus
}
).ToList();
... which shows me the return item statuses LEFT OUTER JOINed to the test statuses, but I can't figure out how to get the grouping and counting to work.
As you do not use the Return table at all, I would skip it. You have this query
SELECT
ri.[ReturnItemStatus] AS ItemStatus,
rit.[ReturnItemTestStatus] AS TestStatus,
COUNT(*) as ComboCount
FROM
[ReturnItem] ri
LEFT OUTER JOIN [ReturnItemTest] rit ON ri.ReturnItemId = rit.ReturnItemId
GROUP BY
ri.[ReturnItemStatus], rit.[ReturnItemTestStatus]
While you can just append grouping to your query, it may not be the best approach. You explicitely define joining keys even when that should not be necessary. In your case you can have at most one test per item so you should be able to write this:
ctx.ReturnItems
.Select(ri => new { ri.ReturnItemStatus, ri.ReturnItemTest.ReturnItemTestStatus })
.GroupBy(x => x, (x, y) => new { x.ReturnItemStatus, x.ReturnItemTestStatus, Count = y.Count() })
Note, that ri.ReturnItemTest.ReturnItemTestStatus is executed on sql server and it would return null when ReturnItemTest is null as a default behaviour of the server.
This is how I managed to do it in the end:
var returns = (
// Grab from returns table
from r in ctx.Returns
// Inner join with return items
join ri in ctx.ReturnItems on r.ReturnID equals ri.ReturnID
// Filter down by return 'closed on' date
where (
r.ClosedOn > startDate &&
r.ClosedOn <= endDate
)
// Join with return item tests. The 'into' clause is powerful and should be used regularly for complex queries;
// really, the lack of an 'into' projection clause can usually be thought of as shorthand. Here, 'into' projects
// the 0..n join hierarchically as an IEnumerable in what is called a 'group join'.
join rit in ctx.ReturnItemTests on ri.ReturnItemID equals rit.ReturnItemID into ritGroupJoined
// 'Flatten out' the join result with the 'from' clause, meaning that group join results with eg. 3 matches will
// cause 3 items in the resultant enumeration, and group join results with zero matches will cause zero items
// in the resultant enumeration. The .DefaultIfEmpty() method means that these results will instead cause one
// item in the resultant enumeration, having the default value for that type (ie. null, as it's a reference type).
// Note that without the 'into' group join above, it's not possible to access the join results with zero matches as
// they are automatically discarded from the results during the default 'inner join'-style flattening.
from rit in ritGroupJoined.DefaultIfEmpty()
// Project these results into an intermediary object to allow ReturnItemTestStatus to be null (as a int? type);
// without this, we couldn't group them because any grouped items whose ReturnItemTestStatus was null would cause
// a type error, null being an invalid value for the ReturnItemTests.ReturnItemTestStatus property (an int type).
select new {
ReturnItemStatus = ri.ReturnItemStatus,
ReturnItemTestStatus = rit == null ? null : (TestStatusEnum?)rit.ReturnItemTestStatus,
} into retData
// Finally, we can now group this flattened data by both item status and item test status; to group by multiple
// fields in LINQ, use an anonymous type containing the fields to group by.
group retData by new { retData.ReturnItemStatus, retData.ReturnItemTestStatus } into retGrouped
// ... and project into an object to get our item status counts.
select new
{
ReturnItemStatus = retGrouped.Key.ReturnItemStatus,
ReturnItemTestStatus = retGrouped.Key.ReturnItemTestStatus,
Count = retGrouped.Count()
}
).ToList();

Linq return distinct values from table if not exist in another

I am trying to return all distinct rows from Staging below where Staging.CenterCode does not exist in Centers.CenterCode.
At the moment Stagings has around 850 distinct CenterCodes and Centers is empty so I should be getting all of the distinct rows, but count begs to differ :)
Any ideas?
var query =
(from s in db.Stagings
join t in db.Centers on s.CenterCode equals t.CenterCode into tj
from t in tj.DefaultIfEmpty()
where s.CenterCode != t.CenterCode
select s.CenterCode).Distinct();
var c = query.Count();
I only need the unique columns from staging so not sure if I actually need a join with the above as I am not ever using data returned from Centers - I have however tried both and get the same 0 value for count.
Any ideas?
I would not use a join, but use a Contains.
var centerCodesQuery = db.Centers.CenterCode
.Select(x => x.CenterCode);
var query = db.Staging
.Where(x => !centerCodesQuery.Contains(x.CenterCode))
.Select(x => x.CenterCode)
.Distinct();
var c = query.Count();
the join is an inner join. So, if none of the rows in 1 table match the other table on the specified identifier then it will return 0. In yours you are trying to join 1 table with 850 distinct rows with an empty table. This will return 0.
If you actually want to return only those rows in 1 table that aren't in another you can use Except:
var query = (from s in db.Stagings
select s.CenterCode)
.Except(from t in db.Centers
select t.CenterCode);
var c = query.Count();
Looks like you are trying to implement antijoin via left outer join, which is one of the possible ways, but in order to make it work, you need to change
where s.CenterCode != t.CenterCode
to
where t == null

Linq join tables using a flag column

I need join 3 tables using a column flag called cashbillingtype_id, this determines what table will joined.
For example i have this Datatable called [CashBillings]:
cashbilling_id cashbillingtype_id
1 1
2 1
3 2
When:
cashbillingtype_id = 1 means: CashBillingsBills
cashbillingtype_id = 2 means: CashBillingsReturns
cashbillingtype_id = 3 means: CashBillingsCancellations
Now each table (Bills, Returns, Cancellations) have inside a column called cashbillingBRC_total i need to get this column data according the flag main datatable.
I Tryed:
(from CashBillings in _DataTable_Billings.AsEnumerable()
join CashBillingsTypes in _DataTable_BillingsTypes.AsEnumerable()
on CashBillings.Field<Int32>("cashbillingtype_id") equals CashBillingsTypes.Field<Int32>("cashbillingtype_id")
select new
{
cashbilling_id = CashBillings.Field<Int32>("cashbilling_id"),
cashbillingBRC_total = (CashBillingsTypes.Field<Int32>("cashbillingtype_id") == 1 ?
(from CashBillingsBills in _DataTable_BillingsBills.AsEnumerable()
where CashBillingsBills.Field<Int32>("cashbilling_id") == CashBillings.Field<Int32>("cashbilling_id")
select CashBillingsBills.Field<Double>("cashbillingbill_total")).LastOrDefault()
:
(CashBillingsTypes.Field<Int32>("cashbillingtype_id") == 2 ?
(from CashBillingsReturns in _DataTable_BillingsReturns.AsEnumerable()
where CashBillingsReturns.Field<Int32>("cashbilling_id") == CashBillings.Field<Int32>("cashbilling_id")
where CashBillingsReturns.Field<Int32>("cashbillingreturnstatus_id") == 1 // Only Processed
select CashBillingsReturns.Field<Double>("cashbillingreturn_total")).LastOrDefault()
:
(from CashBillingsCancellations in _DataTable_BillingsCancellations.AsEnumerable()
where CashBillingsCancellations.Field<Int32>("cashbilling_id") == CashBillings.Field<Int32>("cashbilling_id")
select CashBillingsCancellations.Field<Double>("cashbillingcancellation_total")).LastOrDefault())
),
}).Aggregate(DataTable_Billings, (dt, result) => { dt.Rows.Add(result.cashbilling_id,
result.cashbillingtype_id,
result.cashbillingtype_name,
result.cashbillingBRCstatus_id,
result.cashbillingBRCstatus_name,
result.cashbillingcustomer_fullname,
result.cellar_name,
result.cashbillingBRC_subtotal,
result.cashbillingBRC_discount,
result.cashbillingBRC_isv,
result.cashbillingBRC_total,
result.cashbillingBRC_date); return dt;
});
But this code is very very low efficient.
Can you check with left joining all tables initially and just operating on fields in select statement?
In case you require information on left join, see here

Implementing the All functionality in SQL

I'm trying to write an SQL query where all of a certain group meets a condition.
Update
The Simplifed Table Structure would look like this
ID, TitleID, BlockFromSale
---------------------------
1 | 1 | true
2 | 1 | true
3 | 1 | true
4 | 2 | false
5 | 2 | true
this table would only return the the items TitleID 1.
In actuality I only need the title ID and not the whole item but either will satisfy the conditions.
How Could I write This Linq Query in SQL?
var Query = Data.Items.GroupBy(t => t.TitleID).Where(i => i.All(b => b.BlockFromSale == true));
I try to look at the Sql Query but it just instantly casts it to an object.
Basically I just Need a query that Grabs out all TitleIDs who for each item BlockFromSale is set to true so for the example from the table above it would only return TitleID, 1
It is possible to see the generated SQL of a LINQ query by using the Log property of the query. How to: Display Generated SQL shows an example of this. Basically, the web site shows this example:
db.Log = Console.Out;
IQueryable<Customer> custQuery =
from cust in db.Customers
where cust.City == "London"
select cust;
foreach(Customer custObj in custQuery)
{
Console.WriteLine(custObj.CustomerID);
}
If you want to check predicate p for all rows of a set you can write
NOT EXISTS (... WHERE NOT(p))
Because All(p) == !Any(!p) in pseudo-syntax.
I would guess that ORMs do it this way, too.
There isn't an easy way to do what your asking but a simple but some what slow way would be to use a subQuery
SELECT DISTINCT i.TitleID
FROM Items i
WHERE i.TitleID not in
(SELECT DISTINCT TitleID
FROM items it
WHERE it.BlockFromSale = 0)
so it will remove the titleids who have a false.

Categories