Construct NHibernate query that orders by a boolean condition - nhibernate

I have two entities: Client, and AccountPlan, that have a 1 to 0..1 relationship. I would like to fetch my Clients, ordered first by Clients that have an AccountPlan, and then by Clients that do not. When I try the following Linq to Nhibernate query:
return NHibernateSession.Current.Query<Client>()
.Where(x => x.SalesRepId == id)
.OrderBy(x => x.AccountPlan == null);
I get a QuerySyntaxException with the following message:
{"Exception of type 'Antlr.Runtime.NoViableAltException' was thrown.
[.OrderBy(.Where(NHibernate.Linq.NhQueryable`1[FIS.AccountManagement.Core.Domain.Client],
Quote((x, ) => (Equal(x.SalesRepId, p1))), ), Quote((x, ) =>
(Equal(x.AccountPlan, ))), )]"}
Here's the mapping relationship between the two entities, if that's important:
public ClientMap()
{
HasOne(x => x.AccountPlan).PropertyRef(r => r.Client);
}
public AccountPlanMap()
{
DynamicInsert();
References(x => x.Client, "EntityID");
}
Does anyone know of a query from one of NHibernate's myriad APIs that will accomplish what I want? Thanks in advance.

two queries in one roundtrip concated together
var clientsWithPlan = NHibernateSession.Current.Query<Client>()
.Where(x => x.SalesRepId == id)
.Where(x => x.AccountPlan != null)
.Future();
var clientsWithoutPlan = NHibernateSession.Current.Query<Client>()
.Where(x => x.SalesRepId == id)
.Where(x => x.AccountPlan == null)
.Future();
return clientsWithPlan.Concat(clientsWithoutPlan);

Using a conditional operator:
return NHibernateSession.Current.Query<Client>()
.Where(x => x.SalesRepId == id)
.OrderBy(x => x.AccountPlan == null ? 1 : 0);

As a workaround, you could retrieve all Clients from the database unordered, then perform the ordering in the application using LINQ to objects:
var clients = NHibernateSession.Current.QueryOver<Client>()
.Where(x => x.SalesRepId == id)
.Fetch(x => x.Account).Eager
.List();
return clients.OrderBy(x => x.AccountPlan == null);

Related

SubQuery nHibernate without collection

For example:
Tree treeAlias = null;
var nonRottenApples = QueryOver.Of<Apple>()
.Where(a => !a.IsRotten)
.Where(a => a.Tree.Id == treeAlias.Id)
.Select(x => x.Id); // <- optional
return NHibernateSession.QueryOver(() => treeAlias)
.Where(t => t.Id.IsIn(ListOfTreeId))
.WithSubquery.WhereExists(nonRottenApples)
.List();
How do SubQuery not collection in Apple ?
I'm researching at various locations, but i not find results for my question.
All results are collections.
My interpretation of your question is that you're trying to query for the trees that don't have rotten apples.
I believe this will work:
var rottenTreeIds = QueryOver.Of<Apple>()
.Where(apple => apple.IsRotten)
.Select(a => a.Tree.Id);
return NHIbernateSession.QueryOver<Tree>()
.Where(t => t.Id.IsIn(ListOfTreeId))
.WithSubquery.WhereProperty(t => t.Id).NotIn(rottenTreeIds)
.List();

fluent hibernate Transformers.AliasToBean is not working as expected

I have three tables. One is the master table: TableA. One table is referenced by TableA called ReferencedTable and lastly a lookup table referenced by ReferencedTable.
I have this query that returns the ten most recent objects as:
TableADTO TableAlias = null;
LookupTableDTO LookupTableAlias = null;
ReferencedDTO ReferencedAlias = null;
dtos = session.QueryOver(() => TableAlias)
.JoinAlias(() => TableAlias.Object, () =>ReferencedAlias)
.JoinAlias(() => ReferencedAlias.ObjectType, () => LookupTableAlias)
.Where(() => ReferencedAlias.PersonId == user.Id &&
(LookupTableAlias.Id != INVOICE_ID ||
LookupTableAlias.Id != FINANCIAL_ID) &&
TableAlias.Status == NEW_STATUS_FLAG &&
ReferencedAlias.ReceivedDate < DateTime.Now)
.Take(10)
.List()
.Select(dto=>
new AbreviatedDTO
{
Id = dto.Referenced.Id,
Field1 = dto.Field1,
Priority = dto.Referenced.Priority,
ReceivedDate = dto.Referenced.ReceivedDate,
Field1 = dto.Referenced.Field1,
Type = dto.Referenced.Lookup.TypeCode,
Status = dto.Status
}).ToList();
This works as expected. However, I thought the the transformation below would work too. It does bring 10 objects but the objects have all default values and are not populated (e.g. AbreviatedDTO.ReceivedDate = DateTime.Minimum). Am I doing something wrong with the QueryOver?
Any help would be appreciated.
Bill N
TableDTO TableAlias = null;
LookupTableDTO LookupTableAlias = null;
ReferencedDTO ReferencedAlias = null;
dtos = session.QueryOver(() => TableAlias)
.JoinAlias(() => TableAlias.Object, () =>ReferencedAlias)
.JoinAlias(() => ReferencedAlias.ObjectType, () => LookupTableAlias)
.Where(() => ReferencedAlias.PersonId == user.Id &&
(LookupTableAlias.Id != INVOICE_ID ||
LookupTableAlias.Id != FINANCIAL_ID) &&
TableAlias.Status == NEW_STATUS_FLAG &&
ReferencedAlias.ReceivedDate < DateTime.Now)
.SelectList(list => list
.Select(x => TableAlias.Field1)
.Select(x => ReferencedAlias.Id)
.Select(x => ReferencedAlias.Field1)
.Select(x => ReferencedAlias.ReceivedDate)
.Select(x => ReferencedAlias.Priority)
.Select(x => LookupTableAlias.TypeCode))
.TransformUsing(Transformers.AliasToBean<AbreviatedDTO>())
.Take(10)
.List<AbreviatedDTO>()
you'll need to define an alias for each selected field same as the propertyname in the resulting dto
AbreviatedDTO alias = null;
// in query
.SelectList(list => list
.Select(() => TableAlias.Field1).WithAlias(() => alias.Field1)

Nhibernate queryover order by random

I'm trying to write a query which returns randomly ordered results. I've found this post Linq Orderby random ThreadSafe for use in ASP.NET which gave me some basic clue how to do that. But i'm getting following exception:
variable 'x' of type 'Accomodations.DAL.Model.Generated.Accomodation' referenced from scope '', but it is not defined
Here is my query:
var query = session.QueryOver<Accomodation>()
.OrderBy(x => (~(x.Id & seed)) & (x.Id | seed)).Asc; // this is the problematic line of code
if (searchParams.District != 0)
query = query.Where(x => x.District.Id == searchParams.District);
if (searchParams.Region != 0)
query = query.Where(x => x.Region.Id == searchParams.Region);
if (searchParams.Location != 0)
query = query.Where(x => x.Location.Id == searchParams.Location);
var futureCount = query.Clone().Select(Projections.RowCount()).FutureValue<int>();
SearchAccomodationResultItem resultItemAlias = null;
var futurePage = query
.SelectList(list => list
.Select(x => x.Id).WithAlias(() => resultItemAlias.Id)
.Select(x => x.AccomodationType.Id).WithAlias(() => resultItemAlias.AccomodationTypeId)
.Select(x => x.Region.Id).WithAlias(() => resultItemAlias.RegionId)
.Select(x => x.Name).WithAlias(() => resultItemAlias.Title)
.Select(x => x.MaxCapacity).WithAlias(() => resultItemAlias.MaxCapacity)
.Select(x => x.MinPrice).WithAlias(() => resultItemAlias.MinPrice)
.Select(x => x.MinStayLength).WithAlias(() => resultItemAlias.MinStayLength)
.Select(x => x.MainImageName).WithAlias(() => resultItemAlias.ImgSrc)
)
.TransformUsing(Transformers.AliasToBean<SearchAccomodationResultItem>())
.Skip(skip)
.Take(searchParams.PageSize)
.Future<SearchAccomodationResultItem>();
searchResults = futurePage.ToList();
numberOfResults = futureCount.Value;
});
Any suggestion will be appreciated. Thanks
Here is a good example of how to do this. This is a technique that I'm currently using.
http://puredotnetcoder.blogspot.com/2011/09/nhibernate-queryover-and-newid-or-rand.html
Edit
Below is taken from the above article and I've modified it slightly to include Skip as well.
public IList<CmsTestimonial> GetRandomTestimonials(int count, int skip) {
return Session
.QueryOver<CmsTestimonial>()
.OrderByRandom()
.Take(count)
.Skip(skip)
.List();
}
To use the above approach with paging, you could store the seed for the user (on a per session basis probably) and then use the RAND(seed) function in SQL - because you use the same seed, it will generate the same sequence of pseudo-random numbers and thus allow paging

NHibernate QueryOver association does not contain item

could someone help me to translate LINQ expression to Nhibernate QueryOver
from m in messages
where !m.Recipients.Any(rcpt => rcpt.IsDeleted && rcpt.User = user)
I tried this
var qry = Session.QueryOver<UserMessage>();
qry.Where(m => m.Recipients.Any(r => !r.IsDeleted && r.User == user));
but got
System.Exception : Unrecognised method call: System.Linq.Enumerable:Boolean Any[TSource](System.Collections.Generic.IEnumerable1[TSource], System.Func2[TSource,System.Boolean]
!m.Recipients.Any(...) translates to a "not exists" sub-query. You will need a couple of aliases to correlate the sub-query with the main query, and the sub-query will need to have a projection to make NHibernate happy.
Try something like this:
UserMessage messageAlias = null;
UserMessage recipientMessageAlias = null;
var subquery = QueryOver.Of<MessageRecipient>()
.JoinAlias(x => x.Message, () => recipientMessageAlias)
.Where(x => x.IsDeleted == true) // your criteria
.Where(x => x.User.Id == userId)
.Where(() => recipientMessageAlias.Id == messageAlias.Id) // correlated subquery
.Select(x => x.Id); // projection
var query = session.QueryOver(() => messageAlias)
.Where(Subqueries.WhereNotExists(subquery));
return query.List();
I managed to it this way:
UserMessage messageAlias = null;
var qry = Session.QueryOver<UserMessage>(() => messageAlias);
UserMessageRecipient recipientAlias = null;
var deletedbyUser = QueryOver.Of(() => recipientAlias)
.Select(x => x.Id)
.Where( () => recipientAlias.Message.Id == messageAlias.Id
&& (recipientAlias.Recipient == query.User && recipientAlias.IsDeleted))
.DetachedCriteria;
qry.Where(Subqueries.NotExists(deletedbyUser));
Try to use the Linq version with session.Query<> instead of QueryOver
var qry = Session.Query<UserMessage>();
qry.Where(m => m.Recipients.Any(r => !r.IsDeleted && r.User == user));

How to do subqueries in nhibernate?

I need to do a subquery on a sub collection but I can't get it to work.
I tried this
Task tAlias = null;
List<Task> result = session.QueryOver<Task>(() => tAlias)
.Where(Restrictions.In(Projections.Property(() => tAlias.Course.Id), courseIds))
.WithSubquery.WhereExists(QueryOver.Of<CompletedTask>().Where(x => x.Student.StudentId == settings.StudentId))
().ToList();
Yet I get
Cannot use subqueries on a criteria
without a projection.
session.QueryOver<Task>(() => tAlias)
.WhereRestrictionsOn(x => x.Course.Id).IsIn(courseIds)
.WithSubquery.WhereExists(QueryOver.Of<CompletedTask>()
.Where(x => x.id == tAlias.id) //not sure how you need to link Task to CompletedTask
.Where(x => x.Student.StudentId == settings.StudentId)
.Select(x => x.id)) //exists requires some kind of projection (i.e. select clause)
.List<Task>();
or if you only want the completedtask then just...
Task taskAlias = null;
session.QueryOver<CompletedTask>()
.JoinAlias(x => x.Task, () => taskAlias)
.WhereRestrictionsOn(() => taskAlias.Course.Id).IsIn(courseIds)
.Where(x => x.Student.StudentId == settings.StudentId)
.List<CompletedTask>();
or look into setting up a student filter on the Task.CompletedTasks collection. I've never used this feature before. I believe you have to enable the filter and set the student parameter before you run the query. Then your Task object would only contain completedTasks by that student...
http://nhibernate.info/doc/nh/en/index.html#filters