NHibernate ICriteria - order by child collection count? - nhibernate

Here is a simple example scenario -
A 'Tag' has many 'Questions'. When I get a list of Tags how do I order by the Tags Question Count using the Criteria API?
I have done this before but haven't touched NH in about 4 months and have forgotten... projections maybe? help!!!
Thanks

Sat down with a fresh pair of eyes and figured it out... Tags are now ordered by a propery on the Tags Question collection (views).. which made alot more sence in my domain than ordering by the count of children
public IList<Tag> GetTop(int numberOfTags)
{
using (ITransaction transaction = Session.BeginTransaction())
{
DetachedCriteria detachedCriteria = DetachedCriteria.For<Tag>()
.CreateCriteria<Tag>(x => x.Questions)
.AddOrder<Question>(x => x.Views, Order.Desc)
.SetMaxResults(numberOfTags)
.SetProjection(Projections.Distinct(Projections.Id()));
IList<Tag> tags = Session.CreateCriteria<Tag>()
.SetFetchMode<Tag>(x => x.Questions,FetchMode.Join)
.Add(LambdaSubquery.Property<Tag>(x => x.Id).In(detachedCriteria))
.SetResultTransformer(new DistinctRootEntityResultTransformer())
.List<Tag>();
transaction.Commit();
return tags;
}
}

Try:
var tags = Session.CreateCriteria(typeof(Tag))
.AddOrder(Order.Asc("Tag.Question.Id")
.List<Tag>();
// If that does not work, try:
var tags = Session.CreateCriteria(typeof(Tag))
.CreateCriteria("Question", "TagQuestion", JoinType.InnerJoin)
.AddOrder(Order.Asc("TagQuestion.Id")
.List<Tag>();
Ordering on joined columns.
Ordering the query criteria.
Edit: Unless you're deciding against it or your already comfortable with the Criteria API, you should take a look at either HQL or NHibernate.Linq:
var tags = Session.Linq<Tag>()
.OrderBy(tag => tag.Question.Id)
.ToList();
Linq to NHibernate: a vast improvement.

Related

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?

QueryOver Many to Many Items

I have 2 entities, linked via a Many to Many called Parent and Child.
In Child I have an IList of Parents,
In Parent I have an IList of Childs.
I am trying to do a query on a list of parents, that is linked to a child.
Conceptually wise, I am looking for something like this:
var Query = session.QueryOver<Parent>()
Query.Where(o => o.Children.Contains(child));
But this won't work, so what ways can I get this to work?
Thanks!
You need to use JoinQueryOver
session.QueryOver<Parent>().JoinQueryOver<Child>(p => p.Childs)
.Where(c => c.Id == child.Id)
var query = session.QueryOver<Parent>()
.Where(o => o.Children.Contains(child));
or
var query = session.Query<Parent>()
.Where(o => o.Children.Contains(child));
or see Vadim

QueryOver to select an entity

I have a question. I have this query:
var query = QueryOver.Of<Item>()
.JoinQueryOver<ItemRelation>(p => p.ItemRelation)
.Where(r => r.Item1.Id == itemId)
How can I return only Item entity but not Item Relation?
Thanks
The query actually should only return Predmet ... The RelacijaPredmeta is a property of it. If you don't want to load it, use lazy loading, or select the properties of Predmet individually.

NHibernate/LINQ - Aggregate query on subcollection

Querying child collections has been a recurring issue in our applications where we use NHibernate (via LINQ). I want to figure out how to do it right. I just tried forever to get this query to work efficiently using LINQ, and gave up. Can someone help me understand the best way to do something like this?
Model: ServiceProvider
HasMany->ServicesProvided
The gotcha here is that the HasMany is mapped as a component, so I can't directly query the ServicesProvided. For posterity's sake, here's the mapping:
public ServiceProviderMap()
{
DiscriminatorValue(ProfileType.SERVICE_PROVIDER.ID);
HasMany(p => p.ServicesProvided)
.Table("ServiceProvider_ServicesProvided")
.KeyColumn("ProfileID")
.Component(spMapping =>
{
spMapping.Map(service => service.ID)
.Not.Nullable();
})
.AsBag();
}
The query I am trying to create would return a collection of the count of each service that is provided. IE: Service1 -> 200, Service2 -> 465, etc.
I was able to get the query working using HQL, so here it is. Note that it just returns the ID of the service that is provided:
select service.ID, count(service)
from ServiceProvider as profile
inner join profile.ServicesProvided as service
group by service.ID
I was able to get the query "working" using LINQ, but it performed atrociously. Here's the code I used (warning - it's ugly).
Func<ServiceProvider, IEnumerable<ServicesProvided>> childSelector = sp => sp.ServicesProvided;
var counts = this._sessionManager.GetCurrentSession().Linq<ServiceProvider>()
.Expand("ServicesProvided")
.SelectMany(childSelector, (t, c) => new { t = t, c = c })
.Select(child => child.c)
.GroupBy(sp => sp.ID)
.Select(el => new { serviceID = el.Key, count = el.Count() });
I would love to learn how to do this correctly, please.
Short of going with HQL, the most elegant solution I can think of would be using a Criteria object. The following will give you what you need and with very low overhead:
ICriteria criteria = this._sessionManager.GetCurrentSession().CreateCriteria(typeof(ServiceProvider), "sp");
//set projections for the field and aggregate, making sure to group by the appropriate value
criteria.CreateAlias("sp.ServicesProvided", "s", JoinType.LeftOuterJoin)
.SetProjection(Projections.ProjectionList()
.Add(Projections.Property("s.ID"), "serviceID")
.Add(Projections.Count("sp.ID"), "count")
.Add(Projections.GroupProperty("s.ID")));
IList<object[]> results = criteria.List();
foreach (object[] entry in results)
{
int id = (int)entry[0], qty = (int)entry[1];
//Do stuff with the values
}

Parent-child relationship with LINQ2SQL and POCO objects

I just started learning LINQ2SQL and one of the first things I wanted to try is a simple parent-child hierarchy, but I can't seem to find a good way to do it. I saw some examples here on SO and i've googled, but I couldn't apply them directly, so I'll explain exactly what i'm trying to accomplish.
Lets use the common example with tags.
Database tables: Post-- Post_Tags -- Tags
I've created a simple Post class so I avoid passing Linq2Sql classes around:
public class Post
{
public int Id {get; set;}
public int Title {get; set;}
public IEnumerable<string> Tags {get; set;}
}
I would like to select 5 latest records from the Posts table, get their related tags and return the IList where each Post has their Tags property filled.
Can you show me a concrete Linq2Sql code how could I do that?
I tried:
IList<Post> GetLatest()
{
return (from p in _db.Posts
orderby p.DateCreated descending
select new Post
{
Id = p.Id,
Title = p.Title,
Tags = p.Post_Tags.Select(pt => pt.Tag.Name)
}).Take(5).ToList();
}
This works but duplicates Post records for each Tag record and I have to duplicate property mapping (Id=p.Id, ...) in every method I user. I then tried this approach, but in this case, I have a roundtrip to DB for every tag:
IQueryable<Post> GetList()
{
return (from p in _db.Posts
select new Post
{
Id = p.Id,
Title = p.Title,
Tags = p.Post_Tags.Select(pt => pt.Tag.Name)
});
}
IList<Post> GetLatest()
{
return (from p in GetList()
orderby p.DateCreated descending
select p).Take(5).ToList();
}
If I were doing it in classic ADO.NET, I would create a stored procedure that returns two resultsets. One with Post records and second with related Tag records. I would then map them in the code (by hand, by DataRelation, ORM, etc.). Could I do the same with LINQ2SQL?
I'm really curious to see some code samples on how do you guys handle such simple hierarchies.
And yes, I would really like to return IList<> objects and my custom classes and not queryable Linq to Sql objects, because I would like to be flexible about the data access code if I for example decide to abandon Linq2Sql.
Thanks.
If you create a DataContext, the parent-child relationship is maintained automatically for you.
i.e. If you model the Posts and Tags and their relationship inside a Linq2Sql DataContext, you can then fetch posts like this:
var allPosts = from p in _db.Posts
orderby p.DateCreated descending
select p;
Then you won't have to worry about any tags at all, because they are accessible as a member of the variable p as in:
var allPostsList = allPosts.ToList();
var someTags = allPostsList[0].Post_Tags;
var moreTags = allPostsList[1].Post_Tags;
And then any repeated instance is then automatically updated across entire DataContext until you ask it to SubmitChanges();
IMO, That's the point of an ORM, you don't re-create the model class and maintain the mapping across many places because you want all those relationships managed for you by the ORM.
As for the roundtrip, if you refrain from any code that explicitly requests a trip to the database, all queries will be stored in an intermediate query representation and only when the data is actually needed to continue, is when the query will be translated to sql and dispatched to the database to fetch results.
i.e. the following code only access the database once
// these 3 variables are all in query form until otherwise needed
var allPosts = Posts.All();
var somePosts = allPosts.Where(p => p.Name.Contains("hello"));
var lesserPosts = somePosts.Where(p => p.Name.Contains("World"));
// calling "ToList" will force the query to be sent to the db
var result = lesserPosts.ToList();
How about if you set your DataLoadOptions first to explicitly load tags with posts? Something like:
IList<Post> GetLatest()
{
DataLoadOptions options = new DataLoadOptions();
options.LoadWith<Post>(post => post.Tags);
_db.LoadOptions = options;
return (from p in _db.Posts
orderby p.DateCreated descending)
Take(5).ToList();
}
List<Post> latestPosts = db.Posts
.OrderByDescending( p => p.DateCreated )
.Take(5)
.ToList();
// project the Posts to a List of IDs to send back in
List<int> postIDs = latestPosts
.Select(p => p.Id)
.ToList();
// fetch the strings and the ints used to connect
ILookup<int, string> tagNameLookup = db.Posts
.Where(p => postIDs.Contains(p.Id))
.SelectMany(p => p.Post_Tags)
.Select(pt => new {PostID = pt.PostID, TagName = pt.Tag.Name } )
.ToLookup(x => x.PostID, x => x.TagName);
//now form results
List<Post> results = latestPosts
.Select(p => new Post()
{
Id = p.Id,
Title = p.Title,
Tags = tagNameLookup[p.Id]
})
.ToList();