Linq to Entities, select parent entities with child entities, where child entities match condtion - vb.net

This seems like it should be a simple thing, but I just can't seem to find an example that does what I need.
I have entities "Person", and "SavedSearch". These are joined correctly with foreign keys, so navigation properties work. A "Person" can include multiple "SavedSearch" entities.
What I want to do, is select a list of all of the "Person" entities, each with a collection of "SavedSearch" entities, where these SavedSearch entities meet a particular condition.
This is the closest I've been able to get...
Dim person_query = From p In db.Person
Where p.SavedSearch.Any(Function(s) s.SendEmails = True)
Select New SavedSearchDetails With {
.PersonID = p.PersonID,
.SavedSearchList = p.SavedSearch.Where(Function(s) s.SendEmails = True)
}
This gives me the correct results, but it seems incorrect having to specify the where condition twice. Checking the SQL generated, I can see that it's using left outer joins, which I don't think should be necessary.
So essentially what I need is a list of "Person" entities, with a list of "SavedSearch" entities, where "SendEmail" is true.
Also I should add. I'm only wanting the "Person" entities if they include the matching "SavedSearch" child entities.

You use the Include method to include child entities in the query result:
Dim peopleWithDavedSearches = From p In db.Person.Include("SavedSearch")
Where p.SavedSearch.Any(Function(s) s.SendEmails)
Select p
Each Person object in peopleWithDavedSearches will then have its SavedSearch property populated if there are any entities to populate it with.
You may also be able todo this:
Dim peopleWithDavedSearches = From p In db.Person.Include(Function(person) person.SavedSearch)
Where p.SavedSearch.Any(Function(s) s.SendEmails)
Select p
I'm not 100% sure whether standard LINQ to Entities offers that functionality these days or you still need an additional reference but you can try it and see.
Note that, if you want to include children of children then you just use dot notation, e.g.
From item In list.Include("Child.GrandChild.GreatGrandChild")
If you want to include descendents on multiple branches then you just call Include more than once, e.g.
From item In list.Include("Child1").Include("Child2")

Related

find nodes with a specific child association

I am looking for a query (lucene, fts-alfresco or ...) to return all the document which have a specific child association (that is not null).
Some context:
Documents of type abc:document have a child-association abc:linkedDocument.
Not all document have an other document linked to them, some have none some have one or multiple.
I need a fast and easy way to get an overview of all the documents that do have at least one document linked to them.
Currently I have a webscript that does what I need, but prefer not to have tons of webscripts which are not business related.
code:
SearchParameters sp = new SearchParameters();
String query = "TYPE:\"abc:document\"";
StoreRef store = StoreRef.STORE_REF_WORKSPACE_SPACESSTORE;
sp.addStore(store);
sp.setLanguage(SearchService.LANGUAGE_FTS_ALFRESCO);
sp.setQuery(query);
ResultSet rs = services.getSearchService().query(sp);
List<NodeRef> nodeRefs = rs.getNodeRefs();
for (NodeRef ref : nodeRefs) {
List<ChildAssociationRef> refs = services.getNodeService().getChildAssocs(ref);
for(ChildAssociationRef chref : refs){
if(chref.getQName().equals(AbcModel.ASSOC_LINKED_DOC)){
logger.debug("Document with linked doc: {}", ref);
break;
}
}
}
Associations aren't query-able so you'll have to do what you are doing, which is essentially checking every node in a result set for the presence of a desired association.
The only improvement I can suggest is that you can ask for the child associations of a specific type which would prevent you from having to check the type of every child association, see How to get all Child associations with a specific Association Type Alfresco (Java)

NHibernate Queryover - Is there any way of getting better SQL out of nHibernate when retrieving this object plus collections graph?

We have a situation where we are trying to retrieve a couple of levels deep for an object graph using QueryOver. So if our top level is ParentEntity and our children are ChildEntitysA, ChildEntitysB and ChildEntitysC then we have something like the following code to retrieve our graph.
var query = session.QueryOver<ParentEntity>(() => pAlias).Where(() => pAlias.Id == key).Future<ParentEntity>();
var queryA = session.QueryOver<ParentEntity>(() => pAlias).Left.JoinAlias(x => x.ChildEntitysA, () => caAlias).Where(() => pAlias.Id == key).Future<ParentEntity>();
var queryB = session.QueryOver<ParentEntity>(() => pAlias).Left.JoinAlias(x => x.ChildEntitysB, () => cbAlias).Where(() => pAlias.Id == key).Future<ParentEntity>();
var queryC = session.QueryOver<ParentEntity>(() => pAlias).Left.JoinAlias(x => x.ChildEntitysC, () => ccAlias).Where(() => pAlias.Id == key).Future<ParentEntity>();
return query.ToList();
This should generate 4 SQL statements in one single call to the database and brings back what we want. However, the SQL it generates does have some inefficiency as all of the child queries will contain in the SELECT statement all the columns of the parent as well as the child entity's columns, something akin to:
SELECT parent.col0, parent.col1, parent.col2, parent.col3, .... parent.col99
FROM parent WHERE .....
SELECT parent.col0, parent.col1, parent.col2, parent.col3, .... parent.col99, childA.col0, childA.col1, childA.col2 .... childA.col99
FROM parent LEFT OUTER JOIN childA ON ....
SELECT parent.col0, parent.col1, parent.col2, parent.col3, .... parent.col99, childB.col0, childB.col1, childB.col2 .... childB.col99
FROM parent LEFT OUTER JOIN childB ON ....
SELECT parent.col0, parent.col1, parent.col2, parent.col3, .... parent.col99, childC.col0, childC.col1, childC.col2 .... childC.col99
FROM parent LEFT OUTER JOIN childC ON ....
yet the query generated for the parent entity already contains all the necessary data! This does become a bit of an efficiency concern if we're returning multiples of the parent and the volume of data per child query rises accordingly (especially as the entities involved are quite large in terms of the number of underlying columns).
So, is there any way we can force nHibernate to not generate SQL to return all the parent entity's values for every single part of the query and just limit it to returning minimal key columns only in the SQL? Is Queryover even the optimal API for this situation?
The way I would suggest, is to use full power of NHibernate features in these combinations:
The first is a complex query.
The second is to profit from lazy loading and batch-size setting
So, in the first case we are about to create one and only one QueryOver (maybe including some subqueries)
It must contain projections, that's essential. it can include (many) Left, Inner joins, can be deeply filtered... We will then get the narrowed SELECT statement, containing only required fields. This results in one SQL query, which must be converted into some (unmapped) DTO object
The Projections and DTO means, that at the end we have to use ResultTransformer, which will convert all coming selected fields into DTO object properties.
In the second case, we are trying to profit from lazy behaviour and batch-size mapping. It could look like this:
1) class/ entity
<class name="ParentEntity" batch-size="25" lazy="true">
...
2) collection
<bag name="Children" lazy="true" batch-size="25"
...
See more in documentation: 19.1.5. Using batch fetching
What we gain here is, that we can create soft queries returning only light objects (these which were mapped). This is the first Select. Then all the properties (many-to-one, one-to-many) are loaded in batches - but only if they are accessed, needed.
This leads to more than 1 SELECT clause, but also there is no inflation of SELECT clauses like 1 + N. If NHibernate will find out, that some of the required properties is already loaded in the session... it won't fire the SELECT any more.
Summary: both approaches are really the way. Try to play arround to find out in which situation you get more from the first or the second. BUT the mapping with lazy="true" and batch-size="25" should be used anyway
Some more links about batch-size:
How to Eager Load Associations without duplication in NHibernate?
NHibernate QueryOver with Fetch resulting multiple sql queries and db hits
Is this the right way to eager load child collections in NHibernate

Need a concept on fetching data with HQL while three or more tables are in use

A small briefing on what I am trying to do.
I have three tables Content(contentId, body, timeofcreation), ContentAttachmentMap(contentId, attachmentId) and Attachment(attachmentId, resourceLocation).
The reason I adopted to create the mapping table because in future application the attachment can also be shared with different content.
Now I am using HQL to get data. My objectives is as follows:
Get All contents with/without Attachments
I have seen some examples in the internet like you can create an objective specific class (not POJO) and put the attribute name from the select statement within its constructor and the List of that Class object is returned.
For e.g. the HQL will be SELECT new com.mydomain.myclass(cont.id, cont.body) ..... and so on.
In my case I am looking for the following SELECT new com.mydomain.contentClass(cont.id, cont.body, List<Attachment>) FROM ...`. Yes, I want to have the resultList contain contentid, contentbody and List of its Attachments as a single result List item. If there are no attachments then it will return (cont.id, contentbody, null).
Is this possible? Also tell me how to write the SQL statements.
Thanks in advance.
I feel you are using Hibernate in a fundamentally wrong way. You should use Hibernate to view your domain entity, not to use it as exposing the underlying table.
You don't need to have that contentClass special value object for all these. Simply selecting the Content entity serves what you need.
I think it will be easier to have actual example.
In your application, you are not seeing it as "3 tables", you should see it as 2 entities, which is something look like:
#Entity
public class Content {
#Id
Long id;
#Column(...)
String content;
#ManyToMany
#JoinTable(name="ContentAttachmentMap")
List<Attachment> attachments;
}
#Entity
public class Attachment {
#Id
Long id;
#Column(...)
String resourceLocation
}
And, the result you are looking for is simply the result of HQL of something like
from Content where attachments IS EMPTY
I believe you can join fetch too in order to save DB access:
from Content c left join fetch c.attachments where c.attachments IS EMPTY

EF: How to do effective lazy-loading (not 1+N selects)?

Starting with a List of entities and needing all dependent entities through an association, is there a way to use the corresponding navigation-propertiy to load all child-entities with one db-round-trip? Ie. generate a single WHERE fkId IN (...) statement via navigation property?
More details
I've found these ways to load the children:
Keep the set of parent-entities as IQueriable<T>
Not good since the db will have to find the main set every time and join to get the requested data.
Put the parent-objects into an array or list, then get related data through navigation properties.
var children = parentArray.Select(p => p.Children).Distinct()
This is slow since it will generate a select for every main-entity.
Creates duplicate objects since each set of children is created independetly.
Put the foreign keys from the main entities into an array then filter the entire dependent-ObjectSet
var foreignKeyIds = parentArray.Select(p => p.Id).ToArray();
var children = Children.Where(d => foreignKeyIds.Contains(d.Id))
Linq then generates the desired "WHERE foreignKeyId IN (...)"-clause.
This is fast but only possible for 1:*-relations since linking-tables are mapped away.
Removes the readablity advantage of EF by using Ids after all
The navigation-properties of type EntityCollection<T> are not populated
Eager loading though the .Include()-methods, included for completeness (asking for lazy-loading)
Alledgedly joins everything included together and returns one giant flat result.
Have to decide up front which data to use
It there some way to get the simplicity of 2 with the performance of 3?
You could attach the parent object to your context and get the children when needed.
foreach (T parent in parents) {
_context.Attach(parent);
}
var children = parents.Select(p => p.Children);
Edit: for attaching multiple, just iterate.
I think finding a good answer is not possible or at least not worth the trouble. Instead a micro ORM like Dapper give the big benefit of removing the need to map between sql-columns and object-properties and does it without the need to create a model first. Also one simply writes the desired sql instead of understanding what linq to write to have it generated. IQueryable<T> will be missed though.

NHibernate CreateCriteria and CreateQuery generates different sql?

I'm new to NHibernate and can't figure out why these two statements generates different sql.
the first one only get the ClientInformation (with Information and Client being Proxies) which is what i want.
return repository
.CreateQuery("from ClientInformation ci where ci.Information.IsMandatory = true and ci.Client.Id = :clientId")
.SetParameter("clientId", clientId)
.List<ClientInformation>();
The second one generates everything. All data is returned for the 3 entities, which is not what i want
return repository.CreateCriteria()
.CreateAlias("Information", "inf")
.CreateAlias("Client", "cli")
.Add(Expression.Eq("cli.Id", clientId))
.Add(Expression.Eq("inf.IsMandatory", true))
.List<ClientInformation>();
What i'm i doing wrong ?
thanks
Actually it all boils down to what you want to do.
First of all the Criteria queries honor the mapping definitions (lazy/eager joins etc) where in constrast HQL queries unless defined otherwise everything is lazy (excluding value properties of course)
Secondly the CreateAlias method defines which entities to join and default behaviour is to also select them.
Note that you are calling
repository.CreateCriteria()
and if that wraps directly to nhSession.CreateCriteria() then you haven't defined exactly what you want to select.
So, try to make this
nhSession.CreateCriteria(typeof(ClientInformation));
which will be translated as 'select only ClientInformation'...