NHibernate query ( hql vs criteria ) - nhibernate

I have an hql query string
from MyTable table
where table.StartTime + table.Duration >= : startTime
and table.MyId = : id
How can I write this without HQL in NHibernate (using criteria)?

This might be of interest regarding the DateTime + TimeSpan issue.
This will work in your case:
QueryOver:
int id = 1;
DateTime startTime = DateTime.Now.AddDays(5.0);
var vacations = session.QueryOver<Vacation>()
.Where(v => v.Employee.Id == id)
.And(v => v.StartDate > startTime
|| (v.StartDate == startTime.Date && v.Duration >= startTime.TimeOfDay))
.List();
ICriteria:
var vacationsCrit = session.CreateCriteria(typeof(Vacation))
.Add(Expression.Eq("Employee.Id", id))
.Add(Expression.Disjunction()
.Add(Expression.Gt("StartDate", startTime))
.Add(Expression.Conjunction()
.Add(Expression.Eq("StartDate", startTime.Date))
.Add(Expression.Ge("Duration", startTime.TimeOfDay))))
.List();
Both will output the exact same SQL. It should be mentioned that you cannot do something like this, as described in the link above:
var vacations = session.QueryOver<Vacation>()
.Where(v => v.Employee.Id == id)
.And(v => v.StartDate.Add(v.Duration) >= startTime) // <-- this will NOT work
.List();

Related

QueryOver use Projections.Concat on GroupBy (NHibernate 3.3.5)

I'm trying to implement a bit complicated sql in QueryOver of NHibernate (3.3.5 version) that would be parsed in SQL Server as:
SELECT
CASE WHEN LTR_ReciboCompania IS NULL
THEN CAST(LTR_Id AS VARCHAR(255))
ELSE LTR_ReciboCompania + '#' + LTR_NumPoliza
END
FROM LotesRecibos
GROUP BY
CASE WHEN LTR_ReciboCompania IS NULL
THEN CAST(LTR_Id AS VARCHAR(255))
ELSE LTR_ReciboCompania + '#' + LTR_NumPoliza
END
I've tried a lot of combinations with Projections.Concat() and Projections.SqlFunction("concat", ...) without success.
Sample 1
var concatProjection = Projections.SqlFunction("concat", NHibernateUtil.AnsiString,
Projections.Property<LoteRecibo>(x => x.ReciboCompania), Projections.Constant("#"), Projections.Property<LoteRecibo>(x => x.NumPoliza));
var groupBy = Projections.Conditional(Restrictions.IsNull(Projections.Property<LoteRecibo>(p => p.ReciboCompania)),
Projections.Cast(NHibernateUtil.String, Projections.Property<LoteRecibo>(p => p.Id)),
concatProjection);
var result = sessionScope.Session.QueryOver<LoteRecibo>()
.Where(r => r.CreationDate.Value.YearPart() == 2021)
.Select(Projections.GroupProperty(groupBy))
.List<object[]>();
In that case, the generated query only generates a parameter for the separator in Select but not in Group By:
exec sp_executesql N'SELECT (case when this_.LTR_ReciboCompania is null then cast( this_.LTR_Id as NVARCHAR(255)) else (this_.LTR_ReciboCompania+#p0+this_.LTR_NumPoliza) end) as y0_ FROM LotesRecibos this_ WHERE datepart(year, this_.LTR_FC) = #p1 GROUP BY (case when this_.LTR_ReciboCompania is null then cast( this_.LTR_Id as NVARCHAR(255)) else (this_.LTR_ReciboCompania+?+this_.LTR_NumPoliza) end)',N'#p0 nvarchar(4000),#p1 int',#p0=N'#',#p1=2021
And more readable:
SELECT (
CASE
WHEN this_.LTR_ReciboCompania IS NULL
THEN cast(this_.LTR_Id AS NVARCHAR(255))
ELSE (this_.LTR_ReciboCompania + '#' + this_.LTR_NumPoliza)
END
) AS y0_
FROM LotesRecibos this_
WHERE datepart(year, this_.LTR_FC) = 2021
GROUP BY (
CASE
WHEN this_.LTR_ReciboCompania IS NULL
THEN cast(this_.LTR_Id AS NVARCHAR(255))
ELSE (this_.LTR_ReciboCompania + ? + this_.LTR_NumPoliza)
END
)
var result = sessionScope.Session.QueryOver<LoteRecibo>()
.Where(r => r.CreationDate.Value.YearPart() == 2022)
.SelectList(list => list
.SelectGroup(p => Projections.Concat(p.CodReciboCompania, "#", p.NumPoliza))
)
.List<object[]>();
Sample 2 (simplified without conditional)
var result = sessionScope.Session.QueryOver<LoteRecibo>()
.Where(r => r.CreationDate.Value.YearPart() == 2022)
.SelectList(list => list
.SelectGroup(p => Projections.Concat(p.CodReciboCompania, "#", p.NumPoliza))
)
.List<object[]>();
It throws the following exception:
An unhandled exception of type 'System.Exception' occurred in NHibernate.dll
Unrecognised method call in expression Concat(new [] {p.CodReciboCompania, "#", p.NumPoliza})
But if I set in Select (not in SelectGroup) it works. And I can't understand how to set the Concat function in the group by part of the query.
I should use QueryOver, because we connect to SQL Server and Oracle DB Servers.
UPDATE (Workaround)
While I don't found a better solution, I'll use the first sample without the constant separator. Really I don't need to separate both fields, in my real query I only use the field to group.
var concatProjection = Projections.SqlFunction("concat", NHibernateUtil.AnsiString,
Projections.Property<LoteRecibo>(x => x.ReciboCompania), Projections.Property<LoteRecibo>(x => x.NumPoliza));
var groupBy = Projections.Conditional(Restrictions.IsNull(Projections.Property<LoteRecibo>(p => p.ReciboCompania)),
Projections.Cast(NHibernateUtil.String, Projections.Property<LoteRecibo>(p => p.Id)),
concatProjection);
var result = sessionScope.Session.QueryOver<LoteRecibo>()
.Where(r => r.CreationDate.Value.YearPart() == 2021)
.Select(Projections.GroupProperty(groupBy))
.List<object[]>();

Replicate SQL result in Linq

I have a simple view:
CREATE VIEW [dbo].[ApplicationSummary]
AS
SELECT
CONVERT(VARCHAR(50), NEWID()) AS ID,
ISNULL(AVG(ApplicationTime), 0) AS 'AvgApplicationTime',
ISNULL(AVG(ResponseTime), 0) AS 'AvgResponseTime',
ISNULL(CAST(1.0 * COUNT(CASE WHEN [IsAccepted] = 1 THEN 1 END) / COUNT(*) AS float), 0) AS 'PctAccepted'
FROM
[Application]
WHERE
(IsValid = 1)
AND (CreatedOn < CAST(GETDATE() AS date)
AND CreatedOn >= CAST(GETDATE()-30 AS date))
The idea is that it outputs 3 variables. The first 2 are simple 'averages', whereas the last one, 'PctAccepted' outputs a ratio.
I'm testing a table containing 4 rows — 3 of them are set to IsAccepted = true, so the result is 0.75 (3/4), which converts to 75%.
I'm trying to remove the need for a view and replicate it using Linq over my Entity Framework class.
Here is the important stuff from the query:
var startDate = DateTime.Today;
var endDate = DateTime.Today.AddDays(-30);
var q = r.Find(x => x.IsValid &&
x.CreatedOn < startDate && x.CreatedOn >= endDate)
.GroupBy(x => x)
.Select(g => new
{
AvgApplicationTime = (int)g.Average(i => i.ApplicationTime),
AvgResponseTime = (int)g.Average(i => i.ResponseTime),
ApprovalRatio = g.Count(i => i.IsAccepted == true) / (double)g.Count()
}).First();
return new ApplicationStats(q.AvgApplicationTime, q.AvgResponseTime, q.ApprovalRatio);
So far, I have the two averages outputting correctly, but the ratio is returning 1 (or 100%).
Initially, I thought it may be a rounding issue but I've outputted the result of this line: g.Count(i => i.IsAccepted == true) and it incorrectly returns 1.
I may have grouping in the wrong place, but am currently struggling to make it work.
Any help appreciated.
Found the answer.
Changed .GroupBy(x => x) to .GroupBy(x => 1).
Also g.Count(i => i.IsAccepted == true) can be simplified to g.Count(i => i.IsAccepted)

how to use subquery in order by with case in nhibernate?

I need to fetch result from DB table using nhibernate with QueryOver for below sql query. The SQL request is like this:
SELECT Id, Name, Address,phone,ispassed
FROM employee WHERE ispassed = 1
ORDER BY
(CASE WHEN id in (select empId from salary where empId in (2,45,65) and Type=5) THEN 0 ELSE 1 END) asc,
Name desc
I am stuck in Order by. So, please give an example to construct nhibernate query for
ORDER BY
(CASE WHEN id in (select empId from salary where empId in (2,45,65)
and Type=5) THEN 0 ELSE 1 END) asc
I have sub query as :
var subquery = QueryOver.Of<salary>()
.Where(x => x.Type == 5 && x.empId.IsIn(2,65,45))
.SelectList(list => list
.Select(Projections.Distinct(Projections.Property<salary>(x => x.empId))));
and when I am adding subquery in main query as :
query.OrderBy(Projections.Conditional(
Restrictions.In(Projections.SubQuery(subquery)),
Projections.Constant(0),
Projections.Constant(1))).Asc();
It is showing error as "The best overloaded method match for 'NHibernate.Criterion.Projections.SubQuery(NHibernate.Criterion.DetachedCriteria)' has some invalid arguments."
I hope that's what you want:
Employee employeeAlias = null;
var subquery = QueryOver.Of<salary>()
.Where(x => x.Type == 5 && x.empId.IsIn(2,65,45))
.And(x => x.empId == employeeAlias.Id)
.Select(x => x.empId);
var query = session.QueryOver(() => alias)
.Where(x => x.IsPassed)
.OrderBy(Projections.Conditional(
Subqueries.Exists(subquery.DetachedCriteria),
Projections.Constant(0),
Projections.Constant(1))
).Asc
.ThenBy(x => x.Name).Desc
//.Select
//.List
;

nhibernate QueryOver - complex subselect

How can I write this query with QueryOver.
select * from User usr where exists (select ord.ID from Order ord where ord.UserID = usr.ID)
I know we can write subqueries with QueryOver like ..WithSubSelect.WhereProperty(x=>x.ID == subquery.as<int>()). But I want to use field of the main query in the subquery in order to use it in where clause.
Is this possible ?
thank you for your helps
User userAlias = null;
var subquery = QueryOver.Of<Order>()
.Where(o => o.User == userAlias)
// or
.Where(o => o.User.Id == userAlias.Id)
var usersWithOrders = session.QueryOver(() => userAlias)
.WithSubqueries.WhereExists(subquery)
.List();

Converting SQL containing top, count, group and order to LINQ (2 Entities)

Some LINQ queries still puzzle me.
for a table 'Hits' containing two columns, 'Page' and 'Date', I want to find the most Pages with the most rows in a defined slice of time.
In SQL I would use this:
SELECT TOP 10
[Page]
,COUNT([Page]) as Number
FROM dbo.[Hits]
WHERE [Date] >= CONVERT(datetime,'14 Jan 2009')
AND [Date] < CONVERT(datetime,'15 Jan 2009')
Group BY [Page]
Order by Number DESC
In LINQ I got no idea how to approach this, can anyone help me here? I tried to convert it using linqer, but it just shows an error for this expression.
Something like this should work:
(from p in DataContext.Hits
where (p.Date >= minDate) && (p.Date < maxDate)
group p by p.Page into g
select new { Page = g.Key, Number = g.Count() }).OrderByDescending(x => x.Number).Take(10);
var top10hits = objectContext.Hits
.Where(h => minDate <= h.Date && h.Date < maxDate)
.GroupBy(h => h.Page)
.Select(g => new { Page = g.Key, Number = g.Count() })
.OrderByDescending(x => x.Number)
.Take(10);