C# Lambda Left Join - can't get it working - c#

I am trying to convert the following SQL:
select * from business
left outer join permissions on permissions.permid = busid
and anotherid = 17
into a C# lambda expression, then convert it to an enumerable and do another filter on it. I have tried the following code:
IEnumerable<DTO_business> business= db.business
.Join(db.permissions,
bus => bus.busid,
perm => perm.perm_busid,
(bus, perm) => new { bus, perm })
.Where(e => e.perm.anotherid == 17).DefaultIfEmpty()
.AsEnumerable()
.Where(User.IsInRole("Administrator")
.Select(bus =>
new DTO_business()
{
BusinessID = bus.bus.busid.Convert(),
BusinessName = bus.bus.busname.Convert()
});
But I belive it's not working as the where is outside the join. I'm not sure how to actually get that where within the join and then run DefaultIfEmpty() on the join which should give me the left join.
EDIT: The bit I cannot get working is: and anotherid = 17. If I put it in a .Where in linq it filters it completely and is not part of the left join.

For me LINQ join clause are always simple with query syntax. If you prefer it, this is how you can do:-
var result = from permission in db.permissions
join business in db.business
on permission.permid equals business.busid into b
from bus in b.DefaultIfEmpty()
where permission.anotherid == 17
select new DTO_business()
{
BusinessID = bus != null ? bus.busid : 0,
BusinessName = bus != null ? bus.busname : String.Empty
};

Query syntax would be easier imho but you could do it using GroupJoin like this:
var business= db.business
.GroupJoin(
db.permissions,
bus => bus.busid,
perm => perm.perm_busid,
(bus, perm) => new { bus, perm })
.SelectMany(
z => z.permissions.DefaultIfEmpty(),
(x, y) => new { Business = x.Bus, Permission = y })
.Where(z => z.Permission.anotherid == 17)
.Select(s => new DTO_business
{
BusinessID = s.Business.busid.Convert(),
BusinessName = s.Business.busname.Convert()
});
Reference: LEFT OUTER JOIN in LINQ

You can use composite key in the join condition.
from p in db.permissions
join b in db.business
on new { BusId = b.busid, AnotherId= b.anotherId}
equals new { BusId = p.perm_busid, AnotherId = 17 } into all
from b in all.DefaultIfEmpty()
select new DTO_business
{
BusinessID = b.busid, // check for null reference
BusinessName = b.busname // check for null reference
};
You can use composite keys similarly in extension syntax also.
I see in your code you are using Convert in the select function. This is not allowed in Linq that convert to Sql.

Related

How to groupby with inner join using linq lamba expression

I'm trying to convert a sql stored proc to linq. I'm having issues with the groupby and inner joins.
Here is what I've tried:
var r = _context.Table1
.GroupBy(x => new { x.OptionId, x.Years, x.Strike })
.Join(_context.Table2,
oc => oc.OptionId, o => o.OptionId, (oc, o) => new
{
OptionsCosts = oc,
Options = o
}).Where(x => x.Options.OptionType == 1
&& x.Options.QualifierId != null
&& x.Options.CreditingMethod != "xxx")
.Select(y => new DataModel.Table1()
{
Years = y.Select(a => a.OptionsCosts.Years).FirstOrDefault(),
Strike = y.Select(a => a.OptionsCosts.Strike).FirstOrDefault(),
Value = y.Select(a => a.OptionsCosts.Value).FirstOrDefault(),
ChangeUser = y.Select(a => a.OptionsCosts.ChangeUser).FirstOrDefault(),
ChangeDate = DateTime.Now,
OptionId = y.Select(a => a.OptionsCosts.OptionId).FirstOrDefault()
});
Here is the SQL that I'm trying to convert:
SELECT o2.OptionId, o2.Years, o2.Strike, SUM(d2.Weights) as 'TotalWeight', COUNT(*) as 'Counts'
FROM Table1 o2
INNER JOIN #Dates d2 --this is a temp table that just holds dates. I was thinking just a where statement could do it???
ON d2.EffectiveDate = o2.EffectiveDate
INNER JOIN Table2 od2
ON od2.OptionId = o2.OptionId
AND od2.OptionType = 1
AND od2.qualifierid is null
AND od2.CreditingMethod <> 'xxx' --28095
GROUP BY o2.OptionId,o2.Years, o2.Strike
My data is way off so I'm sure I'm doing something wrong.
var table1=_context.Table1
.groupBy(o2=> new{
o2.OptionId
, o2.Years
, o2.Strike
})
.select(s=> new{
s.key.OptionId
, s.key.Years
, s.key.Strike
,TotalWeight=s.sum(x=>x.Weights)
,Counts=o2.count(c=>c.OptionId)
}).tolist();
var result=table1
.Join(_context.Table2,oc => oc.OptionId, o => o.OptionId, (oc, o) => new{ OptionsCosts = oc, Options = o })
.Where(x => x.Options.OptionType == 1
&& x.Options.QualifierId != null
&& x.Options.CreditingMethod != "xxx")
.select(x=> new {
x.oc.OptionId, x.oc.Years, x.oc.Strike, x.oc.TotalWeight, x.oc.Counts
}).tolist();
Small advise, when you rewriting SQL queries, use LINQ Query syntax which is close to SQL and more effective to avoid errors.
var dates = new List<DateTime>() { DateTime.Now }; // fill list
var query =
from o2 in _context.Table1
where dates.Contains(o2.EffectiveDate)
from od2 in _context.Table1.Where(od2 => // another way to join
od2.OptionId == o2.OptionId
&& od2.OptionType == 1
&& od2.qualifierid == null
&& od2.CreditingMethod != "xxx")
group o2 by new { o2.OptionId, o2.Years, o2.Strike } into g
select new
{
g.Key.OptionId,
g.Key.Years,
g.Key.Strike,
Counts = g.Count()
// SUM(d2.Weights) as 'TotalWeight', -this one is not available because dates in memory
};
If you are on start and trying to rewrite procedures on LINQ - EF Core is bad idea. Too limited IQueryable support and usually you will fight for each complex LINQ query.
Try linq2db which has temporary tables support and your stored proc can be rewritten into identical LINQ queries. Or you can use linq2db.EntityFrameworkCore to extend EF Core functionality.
Disclaimer. I’m creator of this extension and one from linq2db creators.

Need to turn LINQ Query from Normal JOIN into RIGHT OUTER JOIN

I have the following query:
var poll = entities.Polls.Join(entities.Descriptors, p => p.DescriptorID, q => q.ID, (p, q) => new
{
Poll = p,
Descriptor = q
}).Join(entities.Media1, p => p.Descriptor.MediaID, q => q.ID, (p, q) => new
{
Poll = p.Poll,
Descriptor = p.Descriptor,
Media = q
}).Join(entities.PollCommentSettings,p => p.Poll.ID,q => q.PollID,(p,q) => new
{
Poll = p.Poll,
Descriptor = p.Descriptor,
Media = p.Media,
CommentSettings = q
}).FirstOrDefault(p => p.Poll.ID == request.PollID);
The problem I am having is that two of the tables that are being joined on might not have an object, but I do not care, and want to still create my top level object.
The two PollCommentSettings and Media1 tables cause this to return nothing if there is no match. What I need to have is the final object to just set these to null if they were not found. I believe this is a right outer join on both of these tables.
I think I figured out how to accomplish what I need by trying some stuff out in LINQpad. I got the following query which I believe accomplishes what I am looking for.
from poll in Polls join descriptor in Descriptors on poll.DescriptorID equals descriptor.ID into pdj
from descriptor in pdj.DefaultIfEmpty()
join media in Media1 on descriptor.MediaID equals media.ID into dmj
from media in dmj.DefaultIfEmpty()
join commentSetting in PollCommentSettings on poll.ID equals commentSetting.PollID into pcj
from commentSetting in pcj.DefaultIfEmpty()
where poll.ID == 28
select new
{
poll,
descriptor,
media,
commentSetting
}

LINQ to SQL Union and then Join

I have tables similar like Employee, Manager, Branch, Division, Employee_history
And let us say two tables Employee and Manager share certain columns like employee_id, name, branch_id
Branch table has branch_id
Division table has division_id
Employee_history has emp_hist_id.
All of these tables are one-many relationships
The SQL I want to convert to LINQ is
select b.branch_id, e.employee_id
from division d
join branch b on d.division_id = b.division_id
join
( select employee_id, branch_id
from EMPLOYEE
UNION
select employee_id, branch_id
from MANAGER
) e
on e.branch_id = b.branch_id
join EMPLOYEE_HISTORY eh on eh.employee_id = e.employee_id
How do i do a UNION and then a join. I have put a simplified version of SQL than what I have. In my actual SQL, there are two or three joins before Branch table and one join after the union
And my LINQ looks like (similar to actual code)
from division in divisions
join brnch in Branch on division.division_id equals brnch.division_id
join empl in (
from emplyee in Employee select new { EmployeeId = emplyee.employee_id}, BrnachId = branch_id).Union(
from mngr in Manager select new { EmployeeId = mngr.employee_id, BrnachId = branch_id)
on brnch.branch_id equals empl.BranchId
join emplhistory in EMPLOYEE_HISTORY on empl.EmployeeId equals emplhistory.employee_id
where division.division_type_id = 10
select new
{
//...
}
I am getting the error:
The type of one of the expressions in the join clause is incorrect. Type inference failed in the call to 'Join'.
In the line that starts with 'join empl in ('
Your syntax for the on caluse of Join is slightly off. In C# it is not:
on division.division_id = brnch.division_id
it needs to be:
on division.division_id equals brnch.division_id
This needs to be changed for all of the on clauses.
In LINQ the order of the statements is a little bit reversed, as opposed to SQL. And when making a .Join() make sure the property names and types(!) are equal. You can't simply join an int on an int?, you have to cast the int to be int? also.
In the example below, I suppose the Branch_ID on Employee and Manager are nullable (int?).
DataContext.GetTable<Employee>()
.Select(e => new { e.ID, e.Name, e.Branch_ID })
.Union(DataContext.GetTable<Manager>()
.Select(m => new { m.ID, m.Name, m.Branch_ID }))
.Join(DataContext.GetTable<Branch>(),
u => new { Branch_ID = u.Branch_ID },
b => new { Branch_ID = (int?)b.ID },
(u, b) => new { u.ID, u.Name, u.Branch_ID, b.Branch_Name })
.Select(j => new { j.ID, j.Name, j.Branch_ID, j.Branch_Name });
And to finish it, I applied this to the actual code:
divisions
.Where(d => d.division_type_id == 10)
.Join(branches,
d => (int?)d.division_id,
b => b.division_id,
(d, b) => new { b.branch_id }) // j1
.Join(employees
.Select(e => new { e.employee_id, e.branch_id })
.Union(managers
.Select(m => new { m.employee_id, m.branch_id })),
j1 => (int?)j1.branch_id,
em => em.branch_id,
(j1, em) => new { em.employee_id, em.branch_id }) // j2
.Join(employeeHistory,
j2 => j2.employee_id,
h => h.employee_id,
(j2, h) => new { j2.employee_id, j2.branch_id, h.history_line }) // j3

How to join 3 tables with lambda expression?

I have a simple LINQ lambda join query but I want to add a 3rd join with a where clause. How do I go about doing that?
Here's my single join query:
var myList = Companies
.Join(
Sectors,
comp => comp.Sector_code,
sect => sect.Sector_code,
(comp, sect) => new {Company = comp, Sector = sect} )
.Select( c => new {
c.Company.Equity_cusip,
c.Company.Company_name,
c.Company.Primary_exchange,
c.Company.Sector_code,
c.Sector.Description
});
I want to add the following SQL command to the above LINQ query and still maintain the projections:
SELECT
sector_code, industry_code
FROM
distribution_sector_industry
WHERE
service = 'numerical'
The 3rd join would be made with Sector table & Distribution_sector_industry on sector_code.
Thanks in advance.
Just a guess:
var myList = Companies
.Join(
Sectors,
comp => comp.Sector_code,
sect => sect.Sector_code,
(comp, sect) => new { Company = comp, Sector = sect })
.Join(
DistributionSectorIndustry.Where(dsi => dsi.Service == "numerical"),
cs => cs.Sector.Sector_code,
dsi => dsi.Sector_code,
(cs, dsi) => new { cs.Company, cs.Sector, IndustryCode = dsi.Industry_code })
.Select(c => new {
c.Company.Equity_cusip,
c.Company.Company_name,
c.Company.Primary_exchange,
c.Company.Sector_code,
c.Sector.Description,
c.IndustryCode
});
Okay, I can't see why you'd want to select sector_code when you already know it, but I think you want this:
var query = from company in Companies
join sector in Sectors
on company.SectorCode equals sector.SectorCode
join industry in DistributionSectorIndustry
on sector.SectorCode equals industry.SectorCode
where industry.Service == "numerical"
select new {
company.EquityCusip,
company.CompanyName,
company.PrimaryExchange,
company.SectorCode,
sector.Description,
industry.IndustryCode
};
Notes:
I've changed it into a query expression as that's a much more readable way of expressing a query like this.
Although the "where" clause comes after the join, assuming this is a LINQ to SQL or Entity Framework query, it shouldn't make any difference
I've lengthened the range variable names for clarity
I've converted your other names into conventional .NET names; you can do this too in your model
For 4 Tables
var query = CurrencyDeposits
.Join(Customers, cd => cd.CustomerId, cus => cus.Id, (cd, cus)
=> new { CurrencyDeposit = cd, Customer = cus })
.Join(Currencies, x => x.CurrencyDeposit.CurrencyId, cr => cr.Id, (x, cr)
=> new { x.CurrencyDeposit, x.Customer, Currency = cr })
.Join(Banks, x => x.CurrencyDeposit.BankId, bn => bn.Id, (x, bn)
=> new { x.CurrencyDeposit, x.Customer, x.Currency, Bank = bn})
.Select(s => new {
s.CurrencyDeposit.Id,
s.Customer.NameSurname,
s.Currency.Code,
s.Bank.BankName,
s.CurrencyDeposit.RequesCode
});
Try something like this...
var myList = ({from a in Companies
join b in Sectors on a.Sector_code equals b.Sector_code
join c in Distribution on b.distribution_code equals a.distribution_code
select new {...});

LINQ : Left Join And Right Join with a double selection

I am looking for a solution to have all the content of the table PART (by adding a right/left join I suppose) in the following LINQ query :
var query = (from p in db.PARTS
join oc in db.OUTPUT_CONTROLS on p.id equals oc.partid
join f in db.FCT on p.fct equals f.id
select new
{ p.id, p.plant, p.unit, p.type, p.num, f.name, oc.datetime, oc.ncr }
into x
group x by new
{ x.id, x.plant, x.unit, x.type, x.num, x.name }
into g
select new
{ g.Key.id, g.Key.plant, g.Key.unit, g.Key.type, g.Key.num, g.Key.name, startdate = g.Min(oc => oc.datetime), endate = g.Max(oc => oc.datetime), sumncr = g.Sum(oc => oc.ncr) })
.OrderByDescending(oc => oc.startdate);
Thanks
I found the solution on my own thanks to this post : LINQ Left Join And Right Join
The solution :
var query = (from p in db.PARTS
join oc in db.OUTPUT_CONTROLS on p.id equals oc.partid into joined
join f in db.FCT on p.fct equals f.id
from j in joined.DefaultIfEmpty()
select new
{ p.id, p.plant, p.unit, p.type, p.num, f.name, j.datetime, j.ncr } into x
group x by new { x.id, x.plant, x.unit, x.type, x.num, x.name } into g
select new { g.Key.id, g.Key.plant, g.Key.unit, g.Key.type, g.Key.num, g.Key.name, startdate = g.Min(oc => oc.datetime), endate = g.Max(oc => oc.datetime), sumncr = g.Sum(oc => oc.ncr) })
.OrderByDescending(oc => oc.startdate);
If you have a SQL where you see a join followed by a GroupJoin, consider using the LINQ GroupJoin.
Quite often you'll see this in situations where you want "Schools with their Students", "Customers with their Orders", "Zoos with their Animals"
It seems that you have 3 tables: Parts, OutputControls and Fcts.
Every Part has zero or more OutputControls, and every OutputControl belongs to exactly one Part, using foreign key PartId: a straightforward one-to-many relation
A Part has a foreign key FctId, that points to the Fct of the part.
You want (some properties of) the Parts, with their OutputControls and its Fct
var result = parts.GroupJoin(outputControls, // GroupJoin Parts and OutputControls
part => part.Id, // from every part take the Id
outputControl => outputControl.PartId, // from every outputControl take the PartId
// result selector: when these Ids match,
// use the part and all its matching outputControls to make one new object:
(part, outputControlsOfThisPart) => new
{
// select the part properties you plan to use:
Id = part.id,
Plant = part.plant,
Unit = part.unit
// the output controls of this part:
OutputControls = outputControlsOfThisPart.Select(outputControl => new
{
// again, select only the output control properties you plan to use
Id = outputControl.Id,
Name = outputControl.Name,
...
})
.ToList(),
// For the Fct, take the Fct with Id equal to Part.FctId
Fct = Fcts.Where(fct => fct.Id == part.Fct)
.Select(fct => new
{
// select only the Fct properties you plan to use
Id = fct.Id,
Name = fct.Name,
...
})
// expect only one such Fct
.FirstOrDefault(),
});

Categories

Resources