convert sql to linq with two tables - c#

select ind.desc,ind.number
from int_goals_df idd, goals_df ind
where idd.dld_number = 123456
and ind.number = idd.ind_number
and ind.categorie = 2
order by follownumber
I'm having a hard time translating this to linq since it is using two tables.
I've currently solved this now imperatively with a foreach loop but not happy with it..
I'm trying to get a list of goals_df that matches with a list of int_goals_df.
Any tips would be greatly appreciated ! Thank you !
EDIT - here is the code I'm using:
//get current GoalDefinitions by selected Goal
var currentGoalDefinition = MyAppAppContext.MyAppAppContextInstance.MyAppContext.GoalDefinitions.FirstOrDefault(
d => d.DLD_GoalDFID == interv.Goal.DLD_GoalenDFID);
// get current intervGoalDefinitions by GoalDefinition
var currentintervGoalDefinitions = MyAppAppContext.MyAppAppContextInstance.MyAppContext.intervGoalDefinitions.Where(
idd => idd.DLD_GoalDFID == currentGoalDefinition.DLD_GoalDFID).OrderBy(idd => idd.IDD_VolgNummer);
intervDefinitionCollection = new ObservableCollection<intervDefinition>(MyAppAppContext.MyAppAppContextInstance.MyAppContext.intervDefinitions.Where(i => i.IND_Categorie == intCategorie));
// filter intervGoalDefinitions by intervDefinitions
var intervDefinitionCollectionTemp = new ObservableCollection<intervDefinition>();
foreach (var currentintervGoalDefinity in currentintervGoalDefinitions)
{
var foundintervGoalDefinitySorted = intervDefinitionCollection.FirstOrDefault(
i => i.IND_intervDFID == currentintervGoalDefinity.IND_intervDFID);
if (foundintervGoalDefinitySorted != null)
intervDefinitionCollectionTemp.Add(foundintervGoalDefinitySorted);
}
intervDefinitionCollection = intervDefinitionCollectionTemp;

assuming NHibernate as ORM and int_goal is a subclass of goal
var results = from idd in session.Query<IntGoals>()
where idd.DlDNumber = 123456 && idd.Category.Id == 2
orderby idd.FollowNumber
select new { idd.Description, idd.Number };

context.int_goals_df.Join(context.goals_df, x => x.ind_number, x => x.number,
(x, y) => new
{
idd = x,
ind = y
})
.Where(x => x.idd.dld_number = 123456 && x.ind.categorie = 2)
.OrderBy(x => x.idd.follownumber)
.Select(x => new
{
x.ind.desc,
x.ind.number
});

quick go - think you need the join
var results = from idd in session.Query<int_goals_df>()
join ind in session.Query<goals_df>()
on idd.ind_number equals ind.ind_number
where idd.DlDNumber = 123456 && idd.Category.Id == 2
orderby idd.FollowNumber
select new { idd.Description, idd.Number };

I tend to use the sql syntax without implicit joins
/*Fields*/
SELECT ind.desc, ind.number
/*Tables*/
FROM int_goals_df idd
INNER JOIN goals_df ind
ON ind.number = idd.ind_number
/*Conditions*/
WHERE idd.dld_number = 123456
AND ind.categorie = 2
/*Order/Grouping*/
ORDER BY follownumber
You can see from Chris's answer this translates more easily to linq.

Related

DefaultIfEmpty() does not handle empty collections

I've been trying to left join the table and they are in a one-to-many relationship.
I have written a SQL query and trying to convert it into LINQ for my ASP.NET Core application.
My sql query is as follows:
SELECT ap.SystemId,
ap.AccessRequiredToId,
cb.AccessAreaManagementId,
ap.EquipmentTagId,
COUNT(ap.Name) [Count]
FROM ApplicationForms ap LEFT JOIN AccessAreaCheckBoxes cb
ON n ap.RecordId = cb.RecordId
WHERE EndDate IS NULL AND (Checked IS NULL OR Checked = 1)
GROUP BY ap.SystemId, ap.AccessRequiredToId, cb.AccessAreaManagementId, ap.EquipmentTagId
SQL Result
And my LINQ is as follows:
var active = _context.ApplicationForms
.Where(w => w.EndDate == null)
.GroupJoin(_context.AccessAreaCheckBoxes
.Where(w => (w.AccessAreaManagement == null || w.Checked == true)),
x => x.RecordId,
y => y.RecordId,
(x, y) => new { ApplicationForms = x, AccessAreaCheckBoxes = y })
.SelectMany(x => x.AccessAreaCheckBoxes.DefaultIfEmpty(),
(x, y) => new { x.ApplicationForms, AccessAreaCheckBoxes = y })
.GroupBy(g => new { g.ApplicationForms.System, g.ApplicationForms.AccessRequiredTo, g.AccessAreaCheckBoxes.AccessAreaManagement, g.ApplicationForms.EquipmentTag })
.Select(s => new RecordViewModel
{
System = s.Key.System.Name,
AccessRequiredTo = s.Key.AccessRequiredTo.Name,
AccessArea = s.Key.AccessAreaManagement.Name,
EquipmentTag = s.Key.EquipmentTag.Name,
Count = s.Count()
}).ToList();
Everything is working well except it doesn't show the rows with the NULL value.
Did I miss out something in my LINQ?
Any help would be greatly appreciated!
This is what I do in the end, post here for your reference.
var active = (from ap in _context.ApplicationForms
join cb in _context.AccessAreaCheckBoxes
on ap.RecordId equals cb.RecordId into j1
from j2 in j1.DefaultIfEmpty()
where ap.EndDate == null
&& (j2.AccessAreaManagement == null || j2.Checked == true)
group new { ap.System, ap.AccessRequiredTo, j2.AccessAreaManagement, ap.EquipmentTag }
by new { System = ap.System.Name, Building = ap.AccessRequiredTo.Name, AccessArea = j2.AccessAreaManagement.Name, Equipment = ap.EquipmentTag.Name } into grp
select new RecordViewModel
{
System = grp.Key.System,
AccessRequiredTo = grp.Key.Building,
AccessArea = grp.Key.AccessArea,
EquipmentTag = grp.Key.Equipment,
Count = grp.Count()
}).ToList();

How to call a local method from the linq query

In my web api i need to execute a method from linq query itself. My linq query code snippet belongs to method is shown below which calls local method to get required data.
var onlineData = (from od in db.RTLS_ONLINEPERSONSTATUS
let zoneIds = db.RTLS_PERSONSTATUS_HISTORY.Where(p => p.person_id == od.PERSONID).OrderByDescending(z => z.stime > startOfThisDay && z.stime < DateTime.Now).Select(z => z.zone_id).ToList()
let zoneIdsArray = this.getZoneList((zoneIds.ToArray()))
let fzones = zoneIdsArray.Select(z => z).Take(5)
select new OnlineDataInfoDTO
{
P_ID = od.PERSONID,
T_ID = (int)od.TAGID,
LOCS = fzones.ToList()
}
public int[] getZoneList(decimal[] zoneIdsArray)
{
int[] zoneIds = Array.ConvertAll(zoneIdsArray, x => (int)x);
List<int> list = zoneIds.ToList();
for (int c = 1; c < zoneIdsArray.Count(); c++)
{
if (zoneIdsArray[c] == zoneIdsArray[c - 1])
{
list.Remove((int)zoneIdsArray[c]);
}
}
return list.ToArray();
}
I am getting exception at let zoneIdsArray = this.getZoneList((zoneIds.ToArray())), is there any way to solve this problem. I got logic to solve my problem from this link(Linq query to get person visited zones of current day ), the given logic is absolutely fine for my requirement but i am facing problem while executing it.
One way to achieve that would be to perform the projection on the client instead of the underlying LINQ provider. This can be done by separating your query into 2 steps:
var peopleStatus =
from od in db.RTLS_ONLINEPERSONSTATUS
let zoneIds = db.RTLS_PERSONSTATUS_HISTORY
.Where(p => p.person_id == od.PERSONID)
.OrderByDescending(z => z.stime > startOfThisDay && z.stime < DateTime.Now)
.Select(z => z.zone_id)
.ToList()
select new
{
Person = od,
ZoneIds = zoneIds,
};
var onlineData =
from od in peopleStatus.ToList()
let zoneIdsArray = this.getZoneList((od.ZoneIds.ToArray()))
let fzones = zoneIdsArray.Select(z => z).Take(5)
select new OnlineDataInfoDTO
{
P_ID = od.Person.PERSONID,
T_ID = (int)od.Person.TAGID,
LOCS = fzones.ToList()
};

Linq left join with group join

I have set of Users and Visits. (So user do visits)
Visit have User navigation property.
I need to find the users who don't visit.
I can do this by finding the users who visit, finding all of the users then taking the difference.
I was trying to find a solution which is faster.
This is what I have right now:
var users = _db.Users.AsNoTracking().Include(c => c.City).Where(x => x.City.Id == city);
var groupedUsers = _db.Visits.AsNoTracking().Include(c => c.City).Include(a=>a.VisitedBy).Where(x => x.City.Id == city).GroupBy(x => x.VisitedBy).Select(group => new { VisitedBy = group.Key, Count = group.Count() });
var visitingUsers = groupedUsers.Select(user => user.VisitedBy);
var dif = users.Except(visitingUsers);
However I was trying GroupJoin as below:
var results = _db.Users.Include(c => c.City).Where(c => c.City.Id == city).
GroupJoin(_db.Visits.Include(c => c.City).Include(u => u.VisitedBy), u => u.Id, v => v.VisitedBy.Id, (u, v) => new { User = u , Visits = v })
.Select(o=>o.User);
But this gives me all of the Users, I want the users who don't exist in the Visit set.
Any help?
You may be able to avoid the correlated sub-query in the other answer by actually doing the left join with null check. Here's a quick example:
var A = new [] { new Foo { Bar = 1 }, new Foo { Bar = 2 }};
var B = new [] { new Foo { Bar = 2 }};
var C = from x in A
join y in B on x.Bar equals y.Bar into z
from y in z.DefaultIfEmpty()
where y == null
select x;
Check the emitted SQL...
I am not too sure if the city filtering is what you are after however the following should achieve what you desire:
var visitsToCity = _db.Visits.Where(v => v.City.Id == city);
var usersOfCity = _db.Users.Where(u => u.City.Id == city);
var nonVisitingUsers = usersOfCity.Where(u => !visitsToCity.Any(v => v.VisitedBy == u));
The last Where combined with the Any should result in a SQL statement like:
SELECT * FROM Users u WHERE u.CityId = #p0 AND
NOT EXISTS(SELECT 1 FROM Visits v WHERE v.CityId = #p0 AND
v.VisitedById = u.Id)
Where #p0 will be supplied with the value of city.

linq after groupby unable to get column values

I am getting data from multiple tables by joining and i want to group data on particular column value but after group by statement i can access my aliases and their properties. What mistake i am making?
public List<PatientHistory> GetPatientHistory(long prid)
{
using(var db = new bc_limsEntities())
{
List<PatientHistory> result =
(from r in db.dc_tresult
join t in db.dc_tp_test on r.testid equals t.TestId into x
from t in x.DefaultIfEmpty()
join a in db.dc_tp_attributes on r.attributeid equals a.AttributeId into y
from a in y.DefaultIfEmpty()
where r.prid == prid
group new {r,t,a} by new {r.testid} into g
select new PatientHistory
{
resultid = r.resultid,
bookingid = r.bookingid,
testid = r.testid,
prid = r.prid,
attributeid = r.attributeid,
result = r.result,
Test_Name = t.Test_Name,
Attribute_Name = a.Attribute_Name,
enteredon = r.enteredon,
Attribute_Type = a.Attribute_Type
}).ToList();
return result;
}
}
You're doing this wrong way. As been said by Jon after grouping the sequences with aliases r,t,a doesn't exist. After grouping you receive the sequence g with sequances of r,t,a in each element of g. If you want get one object from each group (for example most recent) you should try this:
List<PatientHistory> result =
(from r in db.dc_tresult
join t in db.dc_tp_test on r.testid equals t.TestId into x
from t in x.DefaultIfEmpty()
join a in db.dc_tp_attributes on r.attributeid equals a.AttributeId into y
from a in y.DefaultIfEmpty()
where r.prid == prid
group new {r,t,a} by new {r.testid} into g
select new PatientHistory
{
resultid = g.Select(x => x.r.resultid).Last(), // if you expect single value get it with Single()
// .... here add the rest properties
Attribute_Type = g.Select(x => x.a.Attribute_Type).Last()
}).ToList();
I appreciated this question so I thought I would add another potential usage case. I would like feedback on what the cleanest approach is to getting table information through a group operation so that I can project later in the select operation. I ended up combining what the OP did which is to pass objects into his group clause and then used the g.Select approach suggested by YD1m to get table information out later. I have a LEFT JOIN so I'm defending against nulls :
// SQL Query
//DECLARE #idCamp as Integer = 1
//
//select *,
//(select
//count(idActivityMaster)
//FROM tbActivityMasters
//WHERE dftidActivityCategory = A.idActivityCategory) as masterCount
//FROM tbactivitycategories A
//WHERE idcamp = #idCamp
//ORDER BY CategoryName
int idCamp = 1;
var desiredResult =
(from c in tbActivityCategories
.Where(w => w.idCamp == idCamp)
from m in tbActivityMasters
.Where(m => m.dftidActivityCategory == c.idActivityCategory)
.DefaultIfEmpty() // LEFT OUTER JOIN
where c.idCamp == idCamp
group new {c, m} by new { m.dftidActivityCategory } into g
select new
{
idActivityCategory = g.Select(x => x.m == null ? 0 : x.m.dftidActivityCategory).First(),
idCamp = g.Select(x => x.c.idCamp).First(),
CategoryName = g.Select(x => x.c.CategoryName).First(),
CategoryDescription = g.Select(x => x.c.CategoryDescription).First(),
masterCount = g.Count(x => x.m != null)
}).OrderBy(o=> o.idActivityCategory);
desiredResult.Dump("desiredResult");
If I just use a basic group approach I get the results but not the extra column information. At least I can't find it once I group.
var simpleGroup = (from c in tbActivityCategories
.Where(w => w.idCamp == idCamp)
.OrderBy(o => o.CategoryName)
from m in tbActivityMasters
.Where(m => m.dftidActivityCategory == c.idActivityCategory)
.DefaultIfEmpty() // LEFT OUTER JOIN
where c.idCamp == idCamp
group m by m == null ? 0 : m.dftidActivityCategory into g
select new
{
// How do I best get the extra desired column information from other tables that I had before grouping
// but still have the benefit of the grouping?
// idActivityCategory = g.Select(x => x.m == null ? 0 : x.m.dftidActivityCategory).First(),
// idCamp = g.Select(x => x.c.idCamp).First(),
// CategoryName = g.Select(x => x.c.CategoryName).First(),
// CategoryDescription = g.Select(x => x.c.CategoryDescription).First(),
// masterCount = g.Count(x => x.m != null)
idActivityCategory = g.Key,
masterCount = g.Count(x => x != null)
});
simpleGroup.Dump("simpleGroup");
Please tear this up. I'm trying to learn and it just seems like I'm missing the big picture here. Thanks.
UPDATE : Cleaned up by moving the work into the group and making the select more straight forward. If I had known this yesterday then this would have been my original answer to the OP question.
int idCamp = 1;
var desiredResult =
(from c in tbActivityCategories
.Where(w => w.idCamp == idCamp)
from m in tbActivityMasters
.Where(m => m.dftidActivityCategory == c.idActivityCategory)
.DefaultIfEmpty() // LEFT OUTER JOIN
where c.idCamp == idCamp
group new { c, m } by new
{ idActivityCategory = m == null ? 0 : m.dftidActivityCategory,
idCamp = c.idCamp,
CateGoryName = c.CategoryName,
CategoryDescription = c.CategoryDescription
} into g
select new
{
idActivityCategory = g.Key.idActivityCategory,
idCamp = g.Key.idCamp,
CategoryName = g.Key.CateGoryName,
CategoryDescription = g.Key.CategoryDescription,
masterCount = g.Count(x => x.m != null)
}).OrderBy(o => o.idActivityCategory);
desiredResult.Dump("desiredResult");

Translating SQL to lambda with groupby

I'm trying to translate this sql statement
SELECT row, SUM(value) as VarSum, AVG(value) as VarAve, COUNT(value) as TotalCount
FROM MDNumeric
WHERE collectionid = 6 and varname in ('C3INEV1', 'C3INEVA2', 'C3INEVA3', 'C3INVA11', 'C3INVA17', 'C3INVA19')
GROUP BY row
into an EF 4 query using lambda expressions and am missing something.
I have:
sumvars = sv.staticvararraylist.Split(',');
var aavresult = _myIFR.MDNumerics
.Where(r => r.collectionid == _collid)
.Where(r => sumvars.Contains(r.varname))
.GroupBy(r1 =>r1.row)
.Select(rg =>
new
{
Row = rg.Key,
VarSum = rg.Sum(p => p.value),
VarAve = rg.Average(p => p.value),
TotalCount = rg.Count()
});
where the staticvararraylist has the string 'C3INEV1', 'C3INEVA2', 'C3INEVA3', 'C3INVA11', 'C3INVA17', 'C3INVA19' (without single quotes) and the _collid variable = 6.
While I'm getting the correct grouping, my sum, average, & count values aren't correct.
You didn't post your error message, but I suspect it's related to Contains. I've found that Any works just as well.
This should get you quite close:
var result =
from i in _myIFR.MDNumerics
where i.collectionid == _collid && sumvars.Any(v => i.varname == v)
group i by i.row into g
select new {
row = g.Key,
VarSum = g.Sum(p => p.value),
VarAve = g.Average(p => p.value),
TotalCount = g.Count()
};
Try this:
var aavresult = _myIFR.MDNumerics
.Where(r => r.collectionid == _collid && sumvars.Contains(r.varname))
.GroupBy(r1 =>r1.row,
(key,res) => new
{
Row = key,
VarSum = res.Sum(r1 => r1.value),
VarAve = res.Average(r1 => r1.value),
TotalCount = res.Count()
});

Categories

Resources