NHibernate Lambdas joined ordered collection - nhibernate

I have a entity 'Person' a person has a collection of Friends (also Person entities)
I want to get the first 10 Friends of a particular person, ordered by LatestLogin.
My best effort is:
public static IList<Person> GetFriends(Person person, int count)
{
Person personAlias = null;
Person friendAlias = null;
ICriteria criteria = NHibernateSessionManager.Instance.GetSession()
.CreateCriteria(typeof (Person), () => personAlias)
.CreateCriteria(() => personAlias.Friends, () => friendAlias, JoinType.LeftOuterJoin)
.AddOrder(() => friendAlias.LatestLogin, Order.Desc)
.Add<Person>(p => p.ID == person.ID)
.SetMaxResults(count);
return criteria.List<Person>();
}
Which Does grab all the users friends, but they are not ordered by LatestLogin. Any ideas?

I know it may sound weird but the solution is to change the line:
.AddOrder(() => friendAlias.LatestLogin, Order.Desc)
with:
.AddOrder(() => personAlias.LatestLogin, Order.Desc)
You have to see it the other way around in order to understand why this is necessary and not obvious at the beginning.
You are requesting Person objects (personAlias) that have the same 'Parent' friend (friendAlias) with ID == person.ID (.Add(p => p.ID == person.ID)) therefore you need to sort by the personAlias.LatestLogin and NOT the friendAlias.LatestLogin.

Related

LinqToSql OrderBy has no Effect

I am using a LinqToSql-DataSource for a GridView in this way:
wsv.wsv2DataContext db = new wsv.wsv2DataContext();
e.KeyExpression = "id";
e.QueryableSource = (from mitgliedschaft in db.mitgliedschaft
join person in db.person on mitgliedschaft.person_id equals person.id
join institution in db.institution on mitgliedschaft.verein_id equals institution.id
select new
{
vorname = person.vorname,
nachname = person.nachname,
nameVerein = institution.name,
vereinid = mitgliedschaft.verein_id,
id = mitgliedschaft.id,
verbandsMitgliedsNummer = person.verbandsMitgliedsNummer,
strasse = person.strasse,
plz = person.plz,
ort = person.ort,
geburtsdatum = person.geburtsdatum,
geschlechtid = person.geschlechtid,
statusid = mitgliedschaft.statusid,
bezirk_id = mitgliedschaft.bezirk_id,
kreis_id = mitgliedschaft.kreis_id,
person_id = mitgliedschaft.person_id.Value,
deletedFlag = mitgliedschaft.deletedFlag,
stammverein = mitgliedschaft.stammVerein,
eintrittsdatum = mitgliedschaft.eintritt
}).GroupBy(p => p.person_id).Select(p => p.First());
}
Now i want to order the Selection. At first the "stammVerein"-Column of Table "mitgliedschaft" descending AND the Column "eintritt" of Table "mitgliedschaft". I have tried several ways:
wsv.wsv2DataContext db = new wsv.wsv2DataContext();
e.KeyExpression = "id";
e.QueryableSource = (from mitgliedschaft in db.mitgliedschaft
join person in db.person on mitgliedschaft.person_id equals person.id
join institution in db.institution on mitgliedschaft.verein_id equals institution.id
orderby mitgliedschaft.stammVerein descending, mitgliedschaft.eintritt
select new
{
...
}).GroupBy(p => p.person_id).Select(p => p.First());
}
AND:
wsv.wsv2DataContext db = new wsv.wsv2DataContext();
e.KeyExpression = "id";
e.QueryableSource = (from mitgliedschaft in db.mitgliedschaft
join person in db.person on mitgliedschaft.person_id equals person.id
join institution in db.institution on mitgliedschaft.verein_id equals institution.id
select new
{
...
}).GroupBy(p => p.person_id).Select(p => p.First()).OrderByDescending(stamm => stamm.stammverein).ThenBy(eintritt => eintritt.eintrittsdatum);
}
AND:
wsv.wsv2DataContext db = new wsv.wsv2DataContext();
e.KeyExpression = "id";
e.QueryableSource = (from mitgliedschaft in db.mitgliedschaft
join person in db.person on mitgliedschaft.person_id equals person.id
join institution in db.institution on mitgliedschaft.verein_id equals institution.id
select new
{
....
}).OrderByDescending(stamm => stamm.stammverein).ThenBy(eintritt => eintritt.eintrittsdatum).GroupBy(p => p.person_id).Select(p => p.First());
But nothing of this has any Effects ! I am very new in this kind of DataSource and Linq.
Can anyone help me achieving this order ?
Items within a grouped result will not retain their order. Depending on how you want to factor in the ordering, you will need to do it after the group by, and before, and/or after your First...
To accomplish this, it will be easiest if you map the relationships in EF with navigation properties rather than substituting SQL with Linq QL (joins and such)
Using the following base query:
var query = db.mitgliedschaft
.GroupBy(m => m.Person); // Group by related entity, not ID
For instance, after the group by, you will have sets of records grouped by Person. If you want the first Person with an earliest related record:
var result = query.OrderByDescending(g => g.Key.mitgliedschafts.Max(stamm => stamm.stammverein)
.ThenBy(stamm => stamm.eintritt.eintrittsdatum)
.First();
This is taking a wild guess at your schema & entity relationships, but hopefully it will help you work out something that fits. I can only guess at what eintritt is and how it relates to your entity model.
The initial query takes just your base entities that you want to group, and groups them by the related entity. The result of that grouping will be a set of Grouped mitgliedschafts with a key being the Person. To Order those groups by the person with the most recent mitgliedschafts we use an orderby on the Key's associated mitgliedschafts using the Max value for the collection given a descending order request.
The First then gives us the first grouped collection of mitgliedschafts.
Then if you want to sort the resulting list of mitgliedschafts after getting the person with the most recent one:
var result = query.OrderByDescending(g => g.Key.mitgliedschafts.Max(stamm => stamm.stammverein)
.ThenBy(stamm => stamm.eintritt.eintrittsdatum)
.First().OrderByDescending(stamm => stamm.stammverein)
.ThenBy(stamm => stamm.eintritt.eintrittsdatum)
.ToList();
The 2nd set of OrderBy clauses apply to the selected group, or the mitgliedschafts.
To compose the desired view model, Insert a Select() to build the view model from the mitgliedschafts before the ToList().
With the navigation properties this can probably be done without resorting to a group by. On a hunch, something like this should return something similar:
var query = db.Person
.OrderByDescending(p => p.mitgliedschafts.Max(stamm => stamm.stammverien))
.ThenBy(stamm => stamm.eintritt.eintrittsdatum)
.SelectMany(p => p.mitgliedschafts)
.OrderByDescending(stamm => stamm.stammverien)
.ThenBy(stamm => stamm.eintritt.eintrittsdatum)
.Select(stamm => new { ... })
.ToList();
Anyhow, hopefully that gives you some ideas on things to try if you have the navigation properties mapped or can set those up.

NHibernate filter collection by subcollection items

Health record may have Symptom, which consists of some Words. (ER diagram.)
What I need: by given set of Words return Health records with corresponding Symptoms.
I have this code:
public IEnumerable<HealthRecord> GetByWords(IEnumerable<Word> words)
{
var wordsIds = words.Select(w => w.Id).ToList();
Word word = null;
HealthRecord hr = null;
ISession session = NHibernateHelper.GetSession();
{
return session.QueryOver<HealthRecord>(() => hr)
.WhereRestrictionOn(() => hr.Symptom).IsNotNull()
.Inner.JoinAlias(() => hr.Symptom.Words, () => word)
.WhereRestrictionOn(() => word.Id).IsIn(wordsIds)
.List();
}
}
What we should use here is: INNER SELECT, i.e. subquery. We can do that even with many-to-many maping, but the performance will suffer.
The (easier, my prefered) way would be to not use many-to-many mapping. Because with explicitly mapped pairing object SymptomWord, querying would be much more easier.
Word word = null;
Symptom symptom = null;
// the sub SELECT returning column Symptom.Id
var subq = QueryOver.Of<Symptom>(() => symptom)
// just symptoms refering the searched words
.Inner.JoinAlias(() => symptom.Words, () => word)
.WhereRestrictionOn(() => word.Id).IsIn(wordsIds)
// the result of inner select is
.Select(s => symptom.Id);
And in the next step we can use it for filtering:
var list = session
// just query over HealthRecord here
.QueryOver<HealthRecord>()
.WithSubquery
// the ID of referenced Symptom is in that table
.WhereProperty(hr => hr.Symptom.Id)
// and will be filtered with our subquery
.In(subq)
.List<HelthRecord>();
return list;
That should work, also check some similar issue here:
Query on HasMany reference
NHibernate Lazy Loading Limited by Earlier Criteria
Some hint how to re-map many-to-many (because with a pairing table mapped as an object, we can construct similar and simplified construct, resulting in better SQL Statement)
Nhibernate: How to represent Many-To-Many relationships with One-to-Many relationships?

Restrict fluent nhibernate queryover on multiple child entities comparing properties with a single value

I would like to restrict a query using the QueryOver mechanism in Fluent Nhibernat. I found that I can do this using WhereRestrictOn but there seems to be no possibility to compare to just one value. A IsEqual method of sorts.
I quick example might explain better what my issue is
class Parent{
IList<Child1> C1 {get;set;}
IList<Child2> C2 {get;set;}
}
class Child1{
int Id {get;set;}
}
class Child2{
int Id {get;set;}
}
//What I can do
var result = session.QueryOver<Parent>()
.WhereRestrictionOn(x => x.C1.Id).IsIn(new[]{3})
.AndRestrictionOn(x => x.C2.Id).IsIn(new[]{5}).List();
//What I would like to do
var result = session.QueryOver<Parent>()
.WhereRestrictionOn(x => x.C1.Id).IsEqual(3)
.AndRestrictionOn(x => x.C2.Id).IsEqual(5).List();
So basically my issue is that I'm not able to compare with one value but always have to artifically create an array. Is this not possible or am I missing something?
If it is possible please tell me how. If it is not possible I would appreciate an explantion as to why not.
Thanks in advance.
Try this:
Child1 c1Alias = null;
Child2 c2Alias = null;
var result = session.QueryOver<Parent>()
.InnerJoin(x => x.C1, () => c1Alias) // or use Left.JoinAlias
.InnerJoin(x => x.C2, () => c2Alias) // or use Left.JoinAlias
.Where(() => c1Alias.Id == 3)
.And(() => c2Alias.Id == 2)
.List();
I think you want a pair of Subqueries rather than a restriction, but you'll have to map the ParentID value in both Child1 and Child2.
Each SubQuery should return the ParentID where the childID is your search value, and then you can use a conjunction to return the Parent for both children, or null if there isn't one, I suppose.

Nhibernate query<T> / queryover<T> orderby a subquery

I am having issues getting Nhibernate 3.3.2.4000 to generate the correct subquery used in the orderby clause as displayed below:
select *
from dbo.Person p inner join dbo.Task t on p.Task_FK = p.TaskId
order by (select p.CustomerNumber where p.IsMain=1) desc
We have two entities: Task and Person
One task can have N persons related to it. I.e Task has an IList property.
How can I make Nhibernate generate the correct subquery ? I have gotten as far as something like this with the Query API:
query = query.OrderBy(x => x.Persons.Single(t => t.CustomerNumber));
but I am unsure how I can correctly generate the where clause as displayed in the original sql query. Is this perhaps easier done using the queryover api somehow?
Any advice or guidance is most welcome.
Task task = null
Person person = null;
var subquery = QueryOver.Of<Task>()
.Where(t => t.Id == task.Id)
.JoinQueryOver(t => t.Persons, () => person)
.Where(p => p.IsMain)
.Select(() => person.CustomerNumber);
var query = session.QueryOver(() => task)
.OrderBy(Projections.SubQuery(subquery))
.FetchMany(x => x.Persons)
return query.List();

nHibernate QueryOver Subselect or Join

I am trying to use query over to retrieve a collection of entities based upon a join/subquery as per the example below:
var types = new List<ActivityType>{ActivityType.CommentMedia, ActivityType.KeepMedia};
return _sessionFactory.GetCurrentSession()
.QueryOver<Activity>()
.Where(a.Type.IsIn(types))
.WithSubquery.WhereExists(QueryOver.Of<Resource>()
.Where(k => k.MemberKey == userId)
.Where(k => k.ResourceKey == activity.ResourceId)
)
.Take(take)
.List();
In other words retrieve all activities which are in the resource table matching the user and resource id.
I would do this in raw sql by either joining to Resource, or a subquery:
where a.ResourceId in (select resourceKey from resource where resource.memberkey = a.MemberId)
Not sure how to proceed in nhibernate though. Any suggestions?
(We don't want to use a mapping, as we want to keep the Activity entity very simple for performance reasons)
Thanks in advance
Activity activity = null;
return _sessionFactory.GetCurrentSession()
.QueryOver(() => activity)
.Where(a.Type.IsIn(types))
.WithSubquery.WhereProperty(a => a.ResourceId).In(QueryOver.Of<Resource>()
.Where(k => k.MemberKey == userId)
.Where(k => k.ResourceKey == activity.ResourceId)
)
.Take(take)
.List();