I have only the very basics of LINQ. I speak SQL and JDBC, tasked with converting dynamic PL/SQL to LINQ Entity Framework. How can I add conditional WHERE clauses into LINQ queries? Here is a very simplified example (leaving out type info):
Q1 := 'SELECT bDay = b.birthday
address = b.address
FROM (' ;
Q2 := 'SELECT folks.birthday, Address.address
FROM folks,
(SELECT state,
surname AS name
FROM Individuals, Addresses
WHERE Individuals.addrId = Address.id
AND Addresses.state = 'CA' ) find1
,(SELECT state,
surname AS name
FROM Individuals, Addresses
WHERE Individuals.addrId = Address.id
AND Addresses.state = 'NV' ) find2
AND find1.state(+) = folks.state';
IF input_parm = 'RED' THEN
condAddOn :=condAddOn || 'AND folks.vacationHouse IS NOT NULL';
END IF;
IF input_parm = 'BLUE' THEN
condAddOn :=condAddOn || 'AND folks.vacationHouse = 'Cabin';
END IF;
...
OPEN curs FOR Q1 || Q2 || condAddOn ')b';
Trying to figure out the C#/LINQ syntax, here is my attempt (working bottom up):
var find1 = (from addr in Addresses
from indiv in Individuals
where indiv.addrId = addr.addrID
select new
{
indiv.state,
indiv.surname
});
var find1OuterJoin = (from person in folks
join f1 in find1 on person.addrId equals f1.addrID
from f1OutJn in temp.DefaultIfEmpty()
select new
{
f1OutJn.state,
f1OutJn.surname
});
var Q2 = (from person in folks
from addr in addresses
from f1 in find1OuterJoin
where person.addrId == addr.addrId
&& f1.state == folks.state
select new
{
person.birthday
,addr.address
});
var Q1 = (from q2 in Q1
select new
{bDay = b.birthday
,address = b.address
});
I don't know
1) if I introduced Q1 into the Q2 correctly
2) how to introduce the dynamic WHERE clauses
to end up with an equivalent cursor statement:
OPEN curs FOR Q1 || Q2 || condAddOn ')b';
Added: Can I use a functional or expression to include the dynamic bits? I saw a reference to Expression and Expandable(), but unsure.
Added: my attempt at the LINQ queries
When using the where method with link you are returned an IQueryable object. This does not immediately execute the statement, therefore it is possible to do the following:
var results = from person in folks
join addr in addresses
where person.addrId == addr.addrId
select new {
person.birthday,
addr.address
};
if(predicate){
results = from r in results
where /* new condition here */
select r;
}
var resultSet = results.ToList().AsEnumerable();
for other link operators, especially when using lambda linq you can also use the AsQuerably extension method.
such as, but not limited to:
var results = folks.join(address,
person => person.addrId,
addr => addr.addrId
(person, addr) => new {
person.birthday,
addr.address
}).AsQueryable();
if(predicate)
{
results = results.where(/*predicate for the where condition*/);
}
var resultSet = results.ToList().AsEnumerable();
Related
Here I have SQL query, now I am trying to translate it into linq but don't have any idea how to do it and got stuck in getting ChapterId from ChapterQuestion table.
Any help with translation will be grate.
Thank you
Below is my sql query
SELECT CQ.ChapterId,CQS.SetNumber,count(distinct CQ.ChapterQuestionId) as questioncount FROM
[dbo].[ChapterQuestion] AS CQ
JOIN [dbo].[ChapterQuestionSet] AS CQS ON CQ.ChapterQuestionSetId = CQS.ChapterQuestionSetId
WHERE CQ.ChapterId = 1 group by CQS.SetNumber,CQ.ChapterId
Below is my linq
var list = (from CQS in uow.Repository<ChapterQuestionSet>().GetAll().ToList()
join CQ in uow.Repository<ChapterQuestion>().FindBy(x => x.ChapterId == chapterId).ToList()
on CQS.ChapterQuestionSetId equals CQ.ChapterQuestionSetId
group CQ by CQS into G1
select new ChapterQuestionSetVM
{
ChapterQuestionSetId = G1.Key.ChapterQuestionSetId,
Count = G1.Count(t => t.ChapterQuestionSetId != null),
QuestionSetNo = $"Question set {G1.Key.SetNumber}",
ChapterId = // how do i get chapterid from ChapterQuestion
}).ToList();
This is corrected query. I hope Repository.GetAll() returns IQueryable?
This query works only with EF Core 5.x
var query =
from CQS in uow.Repository<ChapterQuestionSet>().GetAll()
join CQ in uow.Repository<ChapterQuestion>().GetAll() on CQS.ChapterQuestionSetId equals CQ.ChapterQuestionSetId
where CQ.ChapterId == 1
group CQ by new { CQS.SetNumber, CQ.ChapterId } into G1
select new ChapterQuestionSetVM
{
ChapterId = G1.Key.ChapterId
QuestionSetNo = $"Question set {G1.Key.SetNumber}",
Count = G1.Select(t => t.ChapterQuestionSetId).Distinct().Count(),
};
var list = query.ToList();
I have a simple SQL Query which I'm trying to translate to linq. But I keep getting
"...is not supported"
The Query is:
SELECT HARDWARE_ID, HWName, HWTyp_FK from T_HARDWARE
left join T_SWInstalled on T_HARDWARE.HARDWARE_ID = T_SWInstalled.SIHardware_FK
and SISWVerzeichnis_FK = 213
where SWInstalled_id is null
My try with linq:
var cni = from hardware in _context.T_HARDWARE
where hardware.T_SWInstalled.Where(si => si.SISWVerzeichnis_FK == _softwareId) == null
select new InstalledOnListItem(hardware.HARDWARE_ID, hardware.HWName);
Is this actually only possible with raw SQL? Thanks
Use join syntax:
var query =
from hardware in _context.T_HARDWARE
join sw in _context.T_SWInstalled
on new {
JoinProperty1 = hardware.HARDWARE_ID,
JoinProperty2 = sw.SISWVerzeichnis_FK
}
equals
new {
JoinProperty1 = sw.SIHardware_FK,
JoinProperty2 = 213
}
where sw.SWInstalled_id == null
select new InstalledOnListItem(hardware.HARDWARE_ID, hardware.HWName);
How can I convert my sql code into Linq format :
How can we perform a select into another select using Linq c# :
My sql code :
SELECT DepartementProjects.Label, a.Number FROM
(SELECT DepartementProjects.Label ,count(1) as Number FROM DepartementProjects
inner join Absences on DepartementProjects.Id = Absences.DepartementProjectID
where Absences.Profil='SHT'
group by DepartementProjects.Label ) a right join DepartementProjects on DepartementProjects.Label = a.Label;
My attempt :
var AbsByDepartmentADM = from department in _dbContext.DepartementProjects
join abs in _dbContext.Absences on department.Id equals abs.DepartementProjectID
into groupedResult
from groupedResultRight in groupedResult.DefaultIfEmpty()
group groupedResultRight by department.Label into grouped
let NumberOfAbsence = grouped.Count(t => t.DepartementProjectID != null)
let WorkedHours = grouped.Sum(a => a.WorkedHours != null ? a.WorkedHours : 0)
select new
{
DepartmentId = grouped.Key,
NumberOfAbsence,
WorkedHours,
AbsencesHours = (8 * NumberOfAbsence - WorkedHours),
};
If you are trying to translate SQL, translate any subselects first, then the main select, and translate in LINQ phrase order. Also, your LINQ adds some values not in the SQL, so I didn't try to change the SQL to match:
var sub = from dept in _dbContext.DepartementProjects
join abs in _dbContext.Absences on dept.Id equals abs.DepartementProjectID into absj
from abs in absj
where abs.Profil == "SHT"
group abs by dept.Label into absg
select new { Label = absg.Key, Number = absg.Count() };
var ans = from dept in _dbContext.DepartementProjects
join a in sub on dept.Label equals a.Label into lnj
from ln in lnj.DefaultIfEmpty()
select new { dept.Label, ln.Number };
Using Linqpad to work out my query, get the above error on:
(from bp in basepay_records
select new { empID = bp.Prep_emp, loc = bp.Prep_loc }).Union
(
from ass in emp_assignments
select new { empID = ass.Prea_emp, loc = ass.Prea_loc }
)
I have tried it with and without the paren on the first query, no diff. This union is part of a larger query, and this will end up sitting in a subquery that is used in a join, so I can't do the usual, though I did it test it as a stand alone query and it failed, saying no definition for Union:
var q1 = from bp in basepay_records select new { empID = bp.Prep_emp, loc = bp.Prep_loc };
var q2 = from ass in emp_assignments select new { empID = ass.Prea_emp, loc = ass.Prea_loc };
q1.Union (q2).Dump ("Union");
I confirmed that all the datatypes match.
Full error message is:
Cannot execute text selection: 'System.Linq.IQueryable' does not contain a definition for 'Union' and the best extension method overload 'System.Linq.ParallelEnumerable.Union(System.Linq.ParallelQuery, System.Collections.Generic.IEnumerable)' has some invalid arguments
Instance argument: cannot convert from 'System.Linq.IQueryable' to 'System.Linq.ParallelQuery'
I have it working now. I had to split it into 2 queries, which seems stupid, but there you go. Let me know if you have a beter way:
var q1 = from mstr in emp_master_records
join loctab in
(
from bp in basepay_records
select new { empID = bp.Prep_emp, loc = bp.Prep_loc }
)
on mstr.Prem_emp equals loctab.empID
where mstr.Prem_email.StartsWith("TEST_USER")
select loctab.loc;
var q2 = from mstr in emp_master_records
join loctab in
(
from ass in emp_assignments
select new { empID = ass.Prea_emp, loc = ass.Prea_loc }
)
on mstr.Prem_emp equals loctab.empID
where mstr.Prem_email.StartsWith("GREGORY_RANDALL")
select loctab.loc;
q1.Union(q2).Dump ("Union");
I am having following query in sql :
SELECT [definition],[pos]
FROM [WordNet].[dbo].[synsets]
where synsetid in(SELECT [synsetid] FROM [WordNet].[dbo].[senses]
where wordid = (select [wordid]FROM [WordNet].[dbo].[words]
where lemma = 'searchString'))
I had tried this for sql to linq :
long x = 0;
if (!String.IsNullOrEmpty(searchString))
{
var word = from w in db.words
where w.lemma == searchString
select w.wordId;
x = word.First();
var sence = from s in db.senses
where (s.senseId == x)
select s;
var synset = from syn in db.synsets
where sence.Contains(syn.synsetId)
select syn;
But I am getting following error at sence.Contains()
Error1:Instance argument: cannot convert from
'System.Linq.IQueryable<WordNetFinal.Models.sense>' to
'System.Linq.ParallelQuery<int>'
Below code:
var sence = from s in db.senses
where (s.senseId == x)
select s;
Returns object of type: WordNetFinal.Models.sense, but in where sence.Contains(syn.synsetId) you are trying to search in it syn.synsetId which is an integer.
So you should change above code to:
var sence = from s in db.senses
where (s.senseId == x)
select s.senseId;
x seems to be of Word type, which is not the type of Id (probably int or long).
You're comparing an entire sense row with a synsetId, which is not correct. You're also splitting the original query into two separate queries by using First() which triggers an evaluation of the expression so far. If you can live with not returning an SQL error if there are duplicates in words, you can write the query as something like this;
if (!String.IsNullOrEmpty(searchString))
{
var wordIds = from word in db.words
where word.lemma == searchString
select word.wordId;
var synsetIds = from sense in db.senses
where wordIds.Contains(sense.wordId)
select sense.synsetId;
var result = (from synset in db.synsets
where synsetIds.Contains(synset.synsetId)
select new {synset.definition, synset.pos}).ToList();
}
The ToList() triggering the evaluation once for the entire query.
You could also just do it using a simpler join;
var result = (from synset in db.synsets
join sense in db.senses on synset.synsetId equals sense.synsetId
join word in db.words on sense.wordId equals word.wordId
select new {synset.definition, synset.pos}).ToList();