C# Linq statement to join two tables and multiple columns - c#

I have two tables; EndToEnd and PartPort. I'd like to get the PartPortA and PartportB data from the same row in EndToEnd, and query Partport with them and get their corresponding PartGid from Partport which could be on any row in the Partport table. So far, I'm able to do this, but I have to do two different LINQ calls but I'd like to reduce it down to one. Here is my code:
// this demonstrates how to join two tables, however only works for one AssetportGid at a time
var part_portGid_a_results = (from icp in entities.EndToEnd
where icp.IntertPortGidA != null &&
icp.IntertPortGidB != null
join ica in entities.PartPort
on icp.PartPortA equals ica.PortGid
select new { icp.PartPortA, ica.PartGid, }).ToList();
var part_portGid_b_results = (from icp in entities.EndToEnd
where icp.IntertPortGidA != null &&
icp.IntertPortGidB != null
join ica in entities.PartPort
on icp.PartPortB equals ica.PortGid
select new { icp.PartPortA, ica.PartGid, }).ToList();
return Json(part_portGid_a_results, JsonRequestBehavior.AllowGet);
What i'd like to do, and I have already tried but got an error is this:
var part_portGid_a_results = (from icp in entities.EndToEnd
where icp.IntertPortGidA != null &&
icp.IntertPortGidB != null
join ica in entities.PartPort
on icp.PartPortA && icp.PartPortB equals ica.PortGid
select new { icp.PartPortA, ica.PartGid, }).ToList();
The error i get is:
Guid? EndToEnd.PartPortB
Error:
Operator '&&' cannot be applied to operands of type 'System.Guid' and 'System.Guid?'

You don't have to use join. If you want to join with a "complex" comparison, just make a Cartesian product (from ... from) and link the rows by a where clause
var part_portGid_results = (from icp in entities.EndToEnd
where icp.IntertPortGidA != null &&
icp.IntertPortGidB != null
from ica in entities.PartPort
where icp.PartPortA == ica.PortGid
|| icp.PartPortB == ica.PortGid
select new { icp.PartPortA, ica.PartGid, }).ToList();

Related

C# Linq Left Outer Join WHERE clause

I have a linq query where i'm trying to return data if the right Sales table doesn't have a record and also only if the Sales.Fallthrough == false && Sales.Date == null.
This is the base of my query and i've tried many different things with this but can't seem to get it to return the required data. Everything I try only seems to return records if there is no Sales OR they match the WERE caluse but not both.
from cr in efContext.Cases
join si in efContext.Sales on cr.CaseId equals si.CaseId into sicr
from sicr in (from si in sicr
where si == null
select si
).DefaultIfEmpty()
where cr.Withdrawn == false
select new
{
CaseId = cr.CaseId,
PropertyAddress = extension.PropertyAddressTownFormat(cr.PropertyAddress1, cr.PropertyTown),
TargetExchangeDate = sicr.TargetExchangeDate == null ? null : sicr1.TargetExchangeDate,
ActualExchangeDate = sicr.ActualExchangeDate,
}).ToList();
EDIT
Missed out a little information.
Also if there is a record in Sales but does not match WHERE clauses then still return the left side and the right side as if there wasn't a record.
Thanks.
Isn't it just...?
from cr in efContext.Cases
join si in efContext.Sales on cr.CaseId equals si.CaseId into sicr
from s in sicr.DefaultIfEmpty()
select new
{
CaseId = cr.CaseId,
PropertyAddress = extension.PropertyAddressTownFormat(cr.PropertyAddress1, cr.PropertyTown),
TargetExchangeDate = s == null || (s.Fallthrough == false && s.Date == null) ? null : s.TargetExchangeDate,
ActualExchangeDate = s == null || (s.Fallthrough == false && s.Date == null) ? null : s.ActualExchangeDate,
};
Please note that you cannot call to ToList since the query returns an anonymous type.

Unable to create a null constant value of type 'System.Int32[]'.

By using the following
PagedData.Products = from p in db.Products
where (from m in p.Manufacturers
where model.man.Contains(m.ManufacturerID)
select m).Any()
where (from s in p.Sizes
where model.size.Contains(s.SizeID)
select s).Any()
where (from c in p.Colors
where model.color.Contains(c.ColorID)
select c).Any()
select p;
i get this error
Unable to create a null constant value of type 'System.Int32[]'. Only
entity types, enumeration types or primitive types are supported in
this context.
I got the point of the error, but i cannot figure out how should i fix it.
The model.man model.size and model.color are arrays of integer, that may be also null.
Since all conditions must be true to pass any Product you should first check if all arrays have any content at all:
if (model.man != null && model.size != null && model.color != null
&& model.man.Any() && model.size.Any() && model.color.Any())
{
PagedData.Products = from p in db.Products ...
Now you won't execute a query if you know upfront that it doesn't return any data anyway. And it will not throw the exception because you never run the query with null arrays.
I would prefer dynamically building the Where clause using method syntax and ifs, but if you wish to embed the conditions inside the query, you need to ensure that IEnumerables that you use for Contains criteria are not null. And that should happen outside the query:
var man = model.man ?? Enumerable.Empty<int>();
var size = model.size ?? Enumerable.Empty<int>();
var color = model.color ?? Enumerable.Empty<int>();
PagedData.Products = from p in db.Products
where (from m in p.Manufacturers
where man.Any() && man.Contains(m.ManufacturerID)
select m).Any()
where (from s in p.Sizes
where size.Any() && size.Contains(s.SizeID)
select s).Any()
where (from c in p.Colors
where color.Any() && color.Contains(c.ColorID)
select c).Any()
select p;
Note that filter.Any() && filter.Contains(...) make no sense and is equivalent to filter.Contans(...). If you want to ignore the empty filter, then you should use !filter.Any() || filter.Contans(...).
So IMO your query should be either like this
var man = model.man ?? Enumerable.Empty<int>();
var size = model.size ?? Enumerable.Empty<int>();
var color = model.color ?? Enumerable.Empty<int>();
PagedData.Products = from p in db.Products
where (from m in p.Manufacturers
where !man.Any() || man.Contains(m.ManufacturerID)
select m).Any()
where (from s in p.Sizes
where !size.Any() || size.Contains(s.SizeID)
select s).Any()
where (from c in p.Colors
where !color.Any() || color.Contains(c.ColorID)
select c).Any()
select p;
or this
var query = db.Products.AsQueryable();
if (model.man != null && model.man.Length > 0)
query = query.Where(p => p.Manufacturers.Any(m => model.man.Contains(m.ManufacturerID)));
if (model.size != null && model.size.Length > 0)
query = query.Where(p => p.Sizes.Any(s => model.size.Contains(s.SizeID)));
if (model.color != null && model.color.Length > 0)
query = query.Where(p => p.Colors.Any(c => model.color.Contains(c.ColorID)));
PagedData.Products = query;

Left outer join IEnumerable<T> to DataTable - parameter cannot be null error

I have this:
var initialRelease = from c in campaignAvailability
where c.reportStatus == "Initial Release"
select c;
var results = from server in initialRelease
join local in table.AsEnumerable()
on server.campaignId equals local.Field<long>("campaignId") into ls
from local in ls.DefaultIfEmpty()
where DateTime.Compare(server.reportDate, local.Field<DateTime>("reportDate")) > 0 || local == null
select server;
//add it to list of campaigns to process
results.Select(m => new { m.campaignId, m.reportDate }).Distinct()
.Select(n => new CampaignReportDate() {
campaignId = n.campaignId,
reportDate = n.reportDate
}).ToList().ForEach(c => campaignsToProcess.Add(c));
Which I want to look like this in SQL:
SELECT a
FROM ienumerable AS a
LEFT OUTER JOIN table AS b
ON
a.id = b.id
WHERE b.some_date > a.some_date
OR b IS NULL
I understand that the where linq clause can't compare a null date. But, as I understand, the || local == null should have taken care of that. What am I missing?
Figured it out. local was coming in as null and I still needed a way to compare it to server.reportDate. I used a ternary operator. And because I was assigning DateTime.MinValue as the default date, I was able to remove || local == null.
where DateTime.Compare(server.reportDate, (local != null) ? (DateTime)local["reportDate"] : DateTime.MinValue) > 0
When doing left joins in linq to objects, you should prefer providing a default value when empty. Then your query doesn't have to make much changes.
var results =
from server in initialRelease
join local in table.AsEnumerable()
on server.campaignId equals local.Field<long>("campaignId") into ls
from local in ls.DefaultIfEmpty(table.NewRow())
where DateTime.Compare(server.reportDate,
local.Field<DateTime?>("reportDate") ?? Datetime.MaxValue) > 0
select server;

How to Create the Left Join by using the group by data

I have three Table One is "Allowance " ,"Balance" and "TimeoffRequests" in these three table common columns are EmployeeId and TimeoffTypeId, Now i need to get the requested hours of one leave type by grouping thier timeoffTypeId and EmployeeId from the table "TimeoffRequests" , and got the "TimeOffHours". for the i wrote the code like
var query = (from tr in TimeOffRequests
where tr.EmployeeID == 9
group tr by new { tr.EmployeeID, tr.TimeOffTypeID } into res
select new
{
EmployeeID = res.Key.EmployeeID,
TimeOffTypeID = res.Key.TimeOffTypeID,
TotalHours = res.Sum(x => x.TimeOffHours)
}).AsEnumerable();
Now I need to join these results with the first table and have to get the all the employees, and timeoffTypes from the UserAllowance and corresponding TimeoffHours from the grouped table. for getting left joined query i wrote like below.
var requestResult = (from UA in UserAllowances
join UB in UserBalances on UA.EmployeeID equals UB.EmployeeID
where UA.TimeOffTypeID == UB.TimeOffTypeID && UA.EmployeeID == 9
&& UA.TimeOffType.IsDeductableType == true // LeftJoin
join rest in query on UA.EmployeeID equals rest.EmployeeID into penidngRequst
from penReq in penidngRequst.DefaultIfEmpty()
where penReq.TimeOffTypeID == UA.TimeOffTypeID
select new EmployeeTimeOffBalanceModel
{
TimeOffTypeID = UA.TimeOffTypeID != null ? UA.TimeOffTypeID : 0,
YearlyAllowanceHrs = (UA.YearlyAllowanceHrs != null) ? UA.YearlyAllowanceHrs : 0,
BalanceHours = UB.BalanceHrs != null ? UB.BalanceHrs : 0,
PendingHours = (decimal)((penReq != null) ? (penReq.TotalHours) : 0),
EmployeeID = UA != null ? UA.EmployeeID : 0,
}).ToList().Distinct();
It is giving only timeOFfType containing in grouped data,even though I wrote leftjoin for the query using the "into" and DefaultIfEmpty() keywords. the results becomes as like:
and by using the "linqPad" editor i found that it is applying the Cross or Outer Join instead of "left join" what will be the reason.
If I remove this line of code " where penReq.TimeOffTypeID
== UA.TimeOffTypeID" this showing all the timeoffTypes with cross join with repeatation like
How can I achieve left join with tables with Grouped data and showing null values if timeofftypes didn't having the any request?
You might want to move the where clause into the the on equals clause as shown below
join rest in query on new { UA.EmployeeID, UA.TimeOffTypeID } equals new { rest.EmployeeID, rest.TimeOffTypeID } into penidngRequst
from penReq in penidngRequst.DefaultIfEmpty()
You can change
where penReq.TimeOffTypeID == UA.TimeOffTypeID
to
where penReq == null || penReq.TimeOffTypeID == UA.TimeOffTypeID

How to convert multiple SQL LEFT JOIN statement with where clause to LINQ

Is there any way to convert following SQL statement into LINQ?
select ve.EntityID
, fin1.FinanceStat as FinanceStat_New
, fin2.FinanceStat as FinanceStat_Old
from ValuationEvents_PIT_New as ve
left join FinStat_New as Fin1
on ve.EntityID = Fin1.EntityID
left join FinStat_Old as Fin2
on ve.EntityID = Fin2.EntityID
where Fin1.FinanceStat ne Fin2.FinanceStat
and Fin2.FinanceStat is not null
and charindex(Fin1.FinanceStat, 'abc') < 1
and charindex(Fin1.FinanceStat, 'xyz') < 1
Here is my version of it, but I need extra pair of eyes to look at it.
var result = (from ve in valuationEventsPit
join fsn in finStatNew on ve.EntityId equals fsn.EntityID into veFsn
from fin1 in veFsn.DefaultIfEmpty()
join fso in finStatOld on ve.EntityId equals fso.EntityID into veFso
from fin2 in veFso.DefaultIfEmpty()
select new
{
ve.EntityId,
FinStatNew1 = fin1 == null ? null : fin1.FinanceStat,
FinStatNew2 = fin2 == null ? null : fin2.FinanceStat
}).
Where(x => x.FinStatNew1 != null &&
x.FinStatNew2 != null &&
x.FinStatNew1 != x.FinStatNew2 &&
!(x.FinStatNew1.Contains("abc")) &&
!(x.FinStatNew1.Contains("xyz"))).ToList();
The reason I am excluding x.FinStatNew1 == null because of the charindex(Fin1.FinanceStat, 'abc') < 1, which will always return 0 if x.FinStatNew1 is not null and 'abc' or 'xyz' is not there and if x.FinStatNew1 is null then it will return null and condition still will be false (null < 0).
Thanks a lot for your help.
I think you could reduce that query even more and rearrange some things to make it more readable. Based on the original query and these are actually LINQ to objects queries, I'd try this:
const string con1 = "abc";
const string con2 = "xyz";
var query =
from ve in valuationEventPit
join fsn in finStatNew on ve.EntityId equals fsn.EntityID
join fso in finStatOld on ve.EntityId equals fso.EntityID
let FinStatNew = fsn.FinanceStat
let FinStatOld = fso.FinanceStat
where FinStatNew != FinStatOld && FinStatOld != null
&& new[]{con1,con2}.All(con => !FinStatNew.Contains(con))
select new { ve.EntityId, FinStatNew, FinStatOld };
The left join is not necessary here. Since you exclude the null values, we could just ignore them then and do the inner join.

Categories

Resources