Convert difficult ordered SQL statement to LINQ (edmx) - sql

Need help with converting next statement to LINQ:
SELECT * FROM comments
WHERE good_id = '19' AND allow = '1'
ORDER BY IF(parent_id = 0, id, parent_id) DESC, id ASC
It's statement show the comment in next order list:
--Comment
--Subcomment (if parent_id != 0)
--Subcomment (if parent_id != 0)
--Comment
--Subcomment (if parent_id != 0)
etc.
But I don't know how to implement it on LINQ. Any ideas?

have not compiled this but I think I am on the right path.
var comments = from c in db.Comments
where c.good_id == 19 && c.allow = "1"
orderby myFunction(c.parent_id, c.id) descending, id ascending
select comments;
public int myFunction(int parentID, in ID)
{
return parent_id == 0 ? id : parent_id;
}

If models have relationships set up correctly then you don't need to do anything fancy
var comments = dataCoontext.Comments
.Where(c => c.GoodId = 19 &&
c.Allow = 1 &&
c.ParentId = 0) //Get top level comments
.OrderBy(c => c.Id);
foreach(var comment in comments){
Console.WriteLine("Comment:" + comment.Text);
GetSubComments(comment);
}
Then use the following
public void GetSubComments(Comment comment){
foreach(var subComment in comment.Children){ //You can apply any sub ordering to Children
Console.WriteLine("Sub comment:" + subComment.Text);
GetSubComments(subComment);
}
}

Related

How do I translate my SQL Query with Having MAX in LINQ?

I'd like to translate this SQL Query in LINQ with EF
SELECT Agts.AgtNum, Agts.AgtLastname, Agts.AgtFirstname, COUNT(Co.CoEnd) FROM [dbo].Agts AS Agts
INNER JOIN [dbo].[Contracts] AS Co ON Agts.AgtNum = Co.AgtNum
GROUP BY Agts.AgtNum, Agts.AgtLastname, Agts.Firstname
HAVING MAX(Co.CoEnd) <= '2020-05-17'
ORDER BY AgtNum asc
I tried that :
public List<AgentToPurge> AgentsToPurge(DateTime datePurge)
{
return (from agent in this.Entities.Agts
join contract in this.Entities.Contracts on agent.AgtNum equals contract.AgtNum
group agent by agent.AgtNum into g
where g.CoEnd <= datePurge
select new AgentToPurge
{
Id = g.Key,
Lastname = g.Key.AgtLastname,
Firstname = g.Key.AgtFirstname,
Contract_Deleted = g.Key.CoEnd.Count()
}).ToList();
}
But the line
where g.CoFin <= datePurge
doesn't work.
I think my "select new" isn't correct either.
Could you help me to solve this ?
Try the following query:
public List<AgentToPurge> AgentsToPurge(DateTime datePurge)
{
return (from agent in this.Entities.Agts
join contract in this.Entities.Contracts on agent.AgtNum equals contract.AgtNum
group contract by new { agent.AgtNum, agent.AgtLastname, agent.AgtFirstname } into g
where g.Max(x => x.CoEnd) <= datePurge
select new AgentToPurge
{
Id = g.Key.AgtNum,
Lastname = g.Key.AgtLastname,
Firstname = g.Key.AgtFirstname,
Contract_Deleted = g.Sum(x => x.CoEnd != null ? 1 : 0)
}).ToList();
}
Note that LINQ query is built from classes and navigation properties and probably you will not need JOIN, if you have properly defined Model.

SQL lambda output not correct using nested select

I have 2 tables, Terms and Site_Program_Term:
Terms columns: Id, SiteId, Name, IsHoliday, DateStart, DateEnd, Year, IsActive
Site_Program_Term columns: Id, ProgName, SiteId, TermId, Year
Using the above 2 tables, I need to display output with primarily below columns
Name, StartDate, EndDate, NumberOfPrograms
I am getting the desired output but the 'NumberOfPrograms' is not showing the correct values. It should give me a count of all the Programs for a set of particular Term, Year and Site.
Thanks in advance.
Below is the base code.
var terms = db.Terms.Where(t => t.SiteId == id && t.IsActive).ToList().Select(t => new
{
Id = t.Id,
SiteId = t.SiteId,
Name = t.Name,
IsHoliday = t.IsHoliday,
DateStart = t.DateStart,
DateEnd = t.DateEnd,
Year = t.Year,
PubProgram = (
db.Site_Program_Term
.Where (spt => spt.SiteId == id && spt.Year == t.Year)
.Select(s => s).Count()
),
}).OrderBy(n => n.DateStart).ToList();
It should give me a count of all the
Programs for a set of particular Term, Year and Site.
But your Where condition only includes Year and Site, but not the Term. So
.Where(spt => spt.SiteId == id && spt.Year == t.Year)
should actually be:
.Where(spt => spt.SiteId == id && spt.Year == t.Year && spt.TermId == t.Id)
Note that if you are by any chance using Entity Framework (Core) and you have properly configured navigation property on the Term class such as
public class Term
{
// ...
public List<Site_Program_Term> SiteProgramTerms { get; set; }
}
then you can rewrite your query as
var terms = db.Terms
.Where(t => t.SiteId == id && t.IsActive)
.Select(t => new
{
Id = t.Id,
SiteId = t.SiteId,
Name = t.Name,
IsHoliday = t.IsHoliday,
DateStart = t.DateStart,
DateEnd = t.DateEnd,
Year = t.Year,
PubProgram = t.SiteProgramTerms.Where(spt => spt.SiteId == id && spt.Year == t.Year).Count()
})
.OrderBy(n => n.DateStart)
.ToList();
Note that this time you don't need to include spt.TermId == t.Id condition, because SiteProgramTerms property contains only site program terms with TermId that matches the Id of the Term. Also note that the first .ToList() should be omitted, because it causes query to be sent to the SQL server as multiple SELECT queries (as opposed to just one SELECT when .ToList() is not present).

Losing Aliases when trying to group-by rows in linq sql query, how to group-by rows in this case

I am a newbie intern way over my head, I promise I have researched this thoroughly and tried many different things. The following linq query works, but I want to group rows by last and first name and then only show the rows that appear more than once. When I un-comment out the group by statement, all my aliases below become unrecognized and changing them to the actual db.table names doesn't help.
var query = from emps in db.Employees
join c in db.Cards on emps.SbiID equals c.SbiID
where c.StateID == 0 && c.CardNumberNumeric > 100000
//group emps by new {emps.Surname, emps.Name};
//orderby grp.Count() // something like 'where grp.Count > 1
select new
{
Surname = emps.Surname,
Name = emps.Name,
CorpID = emps.Identifier,
CardNum = c.CardNumber,
CostCenter = emps.EmployeeUserField.UF13,
Supervisor = (from e in db.Employees
where
e.Identifier.Equals(emps.EmployeeUserField.UF5)
select e.Surname).FirstOrDefault()
+ ", "
+ (from e in db.Employees
where e.Identifier.Equals(emps.EmployeeUserField.UF5)
select e.Name).FirstOrDefault(),
SupervisorID = emps.EmployeeUserField.UF5,
EmpCommence = emps.CommencementDateTime,
CardCommence = c.CommencementDateTime,
WorkPhone = emps.Telephone,
State = (from cf in db.ComboFields
from sp in db.StringProperties
where cf.ComboIndex.Equals(c.StateID)
&& cf.StringID.Equals(sp.StringID)
&& cf.TableName.Equals("Card")
&& cf.FieldName.Equals("StateID")
select sp.DefaultValue).FirstOrDefault()
};
this.tagsGridView.DataSource = query;
this.tagsGridView.DataBind();
I think the problem you're running into is that you're not flattening out your groups appropriately. For example:
var duplicateEmployees = db.Employees
.GroupBy(emp => emp, new EmployeeComparer())
.Where(grp => grp.Count() > 1)
.SelectMany(grp => grp.AsEnumerable());
var duplicateEmployeeInfo =
from emps in duplicateEmployees
join c in db.Cards on emps.SbiID equals c.SbiID
where c.StateID == 0 && c.CardNumberNumeric > 100000
select new
{
... what to select
};
With:
public class EmployeeComparer : IEqualityComparer<Employee>
{
public bool Equals(Employee x, Employee y)
{
return x.Surname == y.Surname && x.Name == y.Name;
}
public int GetHashCode(Employee obj)
{
unchecked { return (17 * obj.Surname.GetHashCode()) ^ (23 * obj.Name.GetHashCode()); }
}
}
This groups the employees by name, finds the groups that have a count > 1, then returns the elements of those groups. No guarantees on performance, but this should solve your issue.

Nested selects in LINQ expression, how to?

I don't know how to work with nested selects in LINQ.
How could I convert this SQl expression to LINQ?
Select i.ID, i.Impression,
(Select COUNT(ImpressionsId)
from DiaryImpressions
where DiaryPostsId = '2' AND ImpressionsId = i.ID) as Num from Impressions i
Seriously? DiaryPostsId is a string? Oh well...
from i in context.Impressions
select new {
i.ID,
i.Impressions,
Num = (from d in context.DiaryImpressions
where d.DiaryPostsId == "2"
&& d.ImpressionsId == i.ID
select d).Count()
}
from ...
select new {
i.Id,
i.Impression,
Count = context.DiaryImpressions.Count(d => d.DiaryPostsId == 2 && d.ImpressionsId == i.Id)
}
If you map your objects properly, you can use child relations directly:
Count = i.DiaryImpressions.Count(d => d.DiaryPostsId == 2)

Linq 2 SQL Syntax Help

I am almost done converting a MySQL app to Linq2SQL but struggling to get my head round this last SQL query
SELECT a.URL, b.URL AS DuplicateURL
FROM Pages a
INNER JOIN Pages b ON a.MetaDescription = b.MetaDescription
AND a.PageID <> b.PageID
WHERE a.DomainID = #reportid
AND b.DomainID = #reportid
AND LENGTH(TRIM(a.MetaDescription)) > 0
AND a.URL < b.URL
ORDER BY a.URL ASC
Could anymore give me some syntax help on how I would create this query in Linq2SQL?
Any help greatly appreciated
It goes like this:
var DuplicatePages =
from a in DBContext.Pages
join b in DBContext.Pages on a.MetaDescription equals b.MetaDescription
where (a.PageID <> b.PageID) && (a.DomainID == ReportID) &&
(b.DomainID == ReportID) && (a.MetaDescription.Trim().Length > 0) &&
(a.URL < b.URL)
orderby a.URL
select new { Url = a.URL, DuplicateUrl = b.URL };
That's pretty straigtforward:
var reportId = ...;
var duplicates =
from a in db.Pages
from b in db.Pages
where a.MetaDescription == b.MetaDescription
where a.PageID != b.PageID
where a.DomainID == reportId
where b.DomainID == reportId
where a.MetaDescription.Trim().Length > 0
where a.URL < b.URL
orderby a.URL
select new { Url = a.URL, DuplicateUrl = b.Url }