Nhibernate queryover order by random - nhibernate

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

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();

NHibernate projection: How to create AliasToBean projection?

I am trying to convert this inefficient query into one that projects into a dto.
Original query looks like this:
var flatFeePolicies = _session.QueryOver<FlatChargeAccessFee>(() => flatChargeAccessFeeAlias)
.JoinAlias(x => x.AgreementAccessFee, () => agreementAccessFeeAlias)
.JoinQueryOver(x => x.ClientPolicy, () => clientPolicyAlias)
.Where(y => agreementAccessFeeAlias.Agreement.Id == request.AgreementId)
.List()
.Select(x => new FlatChargeAccessFeeInfo()
{
FlatChargeAccessFeeId = x.Id,
ClientName = x.ClientPolicy.Bid.Client.Name,
PolicyNumber = x.ClientPolicy.PolicyNumber,
ClientPolicyId = x.ClientPolicy.Id,
AgreementAccessFeeId = x.AgreementAccessFee.Id,
ShouldCheckBeGenerated = x.ShouldCheckBeGenerated,
MonthlyFee = x.MontlyFeeAmount.Amount.ToString(),
PolicyYear = x.ClientPolicy.PolicyNumber.Year
})
.ToList();
I tried it like this:
var flatFeePolicies = _session.QueryOver<FlatChargeAccessFee>(() => flatChargeAccessFeeAlias)
.JoinAlias(x => x.AgreementAccessFee, () => agreementAccessFeeAlias)
.JoinQueryOver(x => x.ClientPolicy, () => clientPolicyAlias)
.Where(y => agreementAccessFeeAlias.Agreement.Id == request.AgreementId)
.SelectList(list => list
.Select(x => x.Id).WithAlias(() => feeInfo.FlatChargeAccessFeeId)
.Select(x => x.ClientPolicy.Bid.Client.Name).WithAlias(() => feeInfo.ClientName)
.Select(x => x.ClientPolicy.PolicyNumber).WithAlias(() => feeInfo.PolicyNumber)
.Select(x => x.ClientPolicy.Id).WithAlias(() => feeInfo.ClientPolicyId)
.Select(x => x.AgreementAccessFee.Id).WithAlias(() => feeInfo.AgreementAccessFeeId)
.Select(x => x.ShouldCheckBeGenerated).WithAlias(() => feeInfo.ShouldCheckBeGenerated)
.Select(x => x.MontlyFeeAmount.Amount.ToString()).WithAlias(() => feeInfo.MonthlyFee)
.Select(x => x.ClientPolicy.PolicyNumber.Year).WithAlias(() => feeInfo.PolicyYear)
)
.TransformUsing(Transformers.AliasToBean<FlatChargeAccessFeeInfo>())
.List<FlatChargeAccessFeeInfo>();
and I am getting an error that variable "x" has been referenced in scope but was not defined. What is the proper syntax to convert this?
After help from Andrew, here is the correct version that works
ClientPolicy clientPolicyAlias = null;
Client clientAlias = null;
Bid bidAlias = null;
AgreementAccessFee agreementAccessFeeAlias = null;
FlatChargeAccessFee flatChargeAccessFeeAlias = null;
FlatChargeAccessFeeInfo feeInfo = null;
var flatFeePolicies = _session.QueryOver<FlatChargeAccessFee>(() => flatChargeAccessFeeAlias)
.JoinAlias(a => a.AgreementAccessFee, () => agreementAccessFeeAlias)
.JoinQueryOver(b => b.ClientPolicy, () => clientPolicyAlias)
.JoinAlias(b=>b.Bid,()=>bidAlias)
.JoinAlias(b=>b.Client, ()=>clientAlias)
.Where(c => agreementAccessFeeAlias.Agreement.Id == request.AgreementId)
.SelectList(list => list
.Select(d => d.Id).WithAlias(() => feeInfo.FlatChargeAccessFeeId)
.Select(e => clientAlias.Name).WithAlias(() => feeInfo.ClientName)
.Select(e => clientAlias.Number).WithAlias(() => feeInfo.ClientNumber)
.Select(f => bidAlias.OptionNumber).WithAlias(() => feeInfo.BidOptionNumber)
.Select(f => bidAlias.Year).WithAlias(()=>feeInfo.PolicyYear)
.Select(g => clientPolicyAlias.Id).WithAlias(() => feeInfo.ClientPolicyId)
.Select(h => agreementAccessFeeAlias.Id).WithAlias(() => feeInfo.AgreementAccessFeeId)
.Select(j => j.ShouldCheckBeGenerated).WithAlias(() => feeInfo.ShouldCheckBeGenerated)
.Select(k => k.MontlyFeeAmount.Amount).WithAlias(()=>feeInfo.MonthlyFee)
)
.TransformUsing(Transformers.AliasToBean<FlatChargeAccessFeeInfo>())
.List<FlatChargeAccessFeeInfo>();
You're close, a few things though:
This select:
.Select(x => x.MontlyFeeAmount.Amount.ToString()).WithAlias(() => feeInfo.MonthlyFee)
will not work. QueryOver attempts to turn your code directly into SQL. If the property does not exist as a column in the database, the query won't work properly (unless you're using a mapped custom type, QueryOver can handle those)
Nested property access won't work either:
.Select(x => x.ClientPolicy.Bid.Client.Name).WithAlias(() => feeInfo.ClientName)
for a similar reason listed above. QueryOver will attempt to turn your property access directly into SQL. You'll need to explicitly join from ClientPolicy to Bid to Client.
In general, remember that you're writing code that's going to be turned into SQL. In fact, I normally write the SQL I want to generate first and then write the QueryOver that corresponds to that. Hope that helps!

Multiple fetch in ThenFetch

I've an associated entity with <many-to-one> and that entity has two <many-to-one> that I want to fetch at once. I can achieve this by this query:
var tshead = session.Query<MainEntity>()
.Fetch(r=>r.FirstAssoc).ThenFetch(p=>p.Other)
.Fetch(r=>r.FirstAssoc).ThenFetch(p=>p.Another)
.Take(10)
.ToList();
As you can see I had to wrote twice .Fetch(r=>r.FirstAssoc)
I'm sure I can avoid this but I cant figure out how. Any idea ?
Thanks !
If you select a specific 'MainEntity' then you can do it using QueryOver like so:
FirstAssoc firstAssoc = null;
Other other = null;
Another another = null;
tshead = session.QueryOver<MainEntity>()
.Where(x => x.Id == id)
.JoinAlias(x => x.FirstAssoc, () => firstAssoc)
.JoinAlias(() => firstAssoc.Other, () => other)
.JoinAlias(() => firstAssoc.Another, () => another)
.SingleOrDefault();
I've written about it here:
http://www.philliphaydon.com/2011/04/nhibernate-querying-relationships-are-depth/
You can't do:
One-Many-Many
only One-Many-One.
I'm not sure you can do Many-Many-One, transforming that would be too difficult.

Linq to NHibernate: Select multiple sums at once

Is there a way to select multiple sums at once using Linq to NHibernate?
Now I have
int? wordCount = (from translation in session.Query<TmTranslation>()
where translation.SatisfiesCondition
select translation.TranslationUnit)
.Sum(x => (int?)(x.WordCount + x.NumberCount)) ?? 0;
int? tagCount = (from translation in session.Query<TmTranslation>()
where translation.SatisfiesCondition
select translation.TranslationUnit)
.Sum(x => (int?)(x.TagCount)) ?? 0;
int? characterCount = (from translation in session.Query<TmTranslation>()
where translation.SatisfiesCondition
select translation.TranslationUnit)
.Sum(x => (int?)(x.CharacterCount)) ?? 0;
which generates three different SQL queries. In SQL I can grab them all three at once, but is there a way to do this in Linq to NHibernate?
Thank you.
this should help get you started with QueryOver way...
ResultDTO dtoAlias = null; //placeholder alias variable
var dto = session.OueryOver<TmTranslation>()
.Where(x => x.SatisfiesCondition)
//Change this to the actual type of Translation property
.JoinQueryOver<Translation>(x => x.Translation)
.SelectList(list => list
//we can sum these columns individually to keep query simple,...add them together later
.SelectSum(x => x.WordCount).WithAlias(() => dtoAlias.WordCountTotal)
.SelectSum(x => x.NumberCount).WithAlias(() => dtoAlias.NumberCountTotal)
//add more select sums to the select list
)
.TransformUsing(Transformers.AliasToBean<ResultDTO>())
.SingleOrDefault<ResultDTO>();
Having multiple aggregate functions within a Select() call works and results in a single SQL command sent to the database. For example:
var result = session.Query<TmAssignment>()
.Select(a => new
{
Words = session.Query<TmTranslation>().Where(s => s.Assignment == a).Sum(u => (int?) u.WordCount) ?? 0,
Others = session.Query<TmTranslation>().Where(s => s.Assignment == a).Sum(u => (int?)(u.TagCount + u.NumberCount)) ?? 0,
}).ToList();
A potentially simpler solution I use is to do an artificial GroupBy then project to an anonymous object:
eg.
session.Query<TmTranslation>()
.Where(o => o.SatisfiesCondition)
.Select(o => o.TranslationUnit)
.GroupBy(o => 1)
.Select(o => new
{
WordCount = o.Sum(x => (int?)(x.WordCount + x.NumberCount)) ?? 0,
TagCount = o.Sum(x => (int?)(x.TagCount)) ?? 0,
CharacterCount = o.Sum(x => (int?)(x.CharacterCount)) ?? 0
})
.Single();
This also produces just a single SQL statement and reduces the duplication nicely.

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