My question is very similar to this one (that wasn't really answered): Nhibernate: distinct results in second level Collection
I have this object model:
class EntityA
{
...
IList<EntityB> BList { get; protected set; }
...
}
class EntityB
{
... does NOT reference its parent EntityA...
IList<EntityC> CList { get; protected set; }
}
They are One-to-Many relations. EntityB and C do not have an object reference to its parent object.
I'd like to fully load the collections by performing something like the following three SQL queries to avoid to Cartesian join:
SELECT id, ... FROM EntityA;
SELECT id, idA, ... FROM EntityB;
SELECT id, idB, ... FROM EntityC;
With that, the DAL has all the information to properly fill the objects. But since EntityB is not aware of who its parent is, it has to be nHibernate who takes care of filling the collections properly.
Can it be done ??
I could do this workaround with the Cartesian Product, but it requires modifying my model to provide a collection setter and that qualifies as a patch for a technical problem with the DAL in my mind.
ICriteria criteria = session.CreateCriteria<EntityA>()
.SetFetchMode("BList", FetchMode.Join)
.SetFetchMode("BList.CList", FetchMode.Join)
.SetResultTransformer(new DistinctRootEntityResultTransformer());
IList<EntityA> listA = criteria.List<EntityA>();
foreach (EntityA objA in listA) {
objA.BList = objA.BList.Distinct().ToList();
foreach (EntityB objB in objB.BList) {
objB.CList = objB.CList.Distinct().ToList();
}
}
Have you tried this syntax:
var entities = session.QueryOver<EntityA>().Where(...).List();
var entityIds = entities.Select(e => e.Id).ToArray();
session.QueryOver<EntityA>()
.WhereRestrictionOn(a => a.Id)
.IsIn(entityIds)
.Fetch(e => e.BList).Eager
.List();
var bEntityIds = entities
.SelectMany(e => e.BList)
.Select(b => b.Id)
.ToArray();
session.QueryOver<EntityB>()
.WhereRestrictionOn(b => b.Id)
.IsIn(bEntityIds).Fetch(e => e.CList).Eager
.List();
This should fire the three selects that you mention. It might seem a wee bit convoluted, but it is taking advantage of the session's first level cache, which ensures that all the entities in the first collection is updated with the loaded collections as they are executed.
Also, you do not pay the penalty of whatever complex query-logic you may have in place for the second and third query. The DB should use the primary index for the second query (maybe even clustered depending on you settings) and a foreign key for the join for extremely low-cost queries.
Related
I've got an NHibernate 4 project with several collection relationships. I'm unit-testing the object model, exercising all the collections. Most work fine, but in one case, the child collection is cascade-saved properly, but on loading the parent entity and examining the collection property, the child collection is empty.
Here are the abbreviated classes. GatewayUser is the parent object, and it has a collection of Student. The collection has a private backing property, and AddStudent/RemoveStudent methods.
Further complications: I'm using the NHibernate.AspNet.Identity library for OAuth2 user management, and GatewayUser inherits from IdentityUser. That in turn inherits from the the library's internal base entity class, which is different from my project's own base class.
public class GatewayUser : IdentityUser
{
public GatewayUser()
{
}
public virtual string FirstName { get; set; }
// ...More value properties and OAuth stuff omitted
// students associated with this user
private IList<Student> _students = new List<Student>();
public virtual IList<Student> Students
{
get { return new ReadOnlyCollection<Student>(_students); }
}
public virtual GatewayUser AddStudent(Student s)
{
if (_students.Contains(s))
return this;
s.GatewayUser = this;
_students.Add(s);
return this;
}
public virtual GatewayUser RemoveStudent(Student s)
{
if (_students.Contains(s))
{
_students.Remove(s);
}
return this;
}
Student is more ordinary; it inherits from my own BaseEntity class, has many value properties, and its own child collection of ProgramApplication items. Interestingly, this collection saves and loads fine; it's got the same structure (private backer, etc.) as the failing collection in GatewayUser.
The mapping is complicated, because the library internally maps its classes with NHiberante.Mapping.ByCode.Conformist classes (which I have no prior experience with).
I'm mapping my own classes with NHibernate automapping, because I have so many classes and properties to map. To get it all working, I copied the library's mapping helper class, and modified it a bit to add my base entity classes to it's list called baseEntityToIgnore. I also had to create a conformist mapping for GatewayUser, since it has a different base entity type, and my automapping wouldn't pick it up.
The unit test looks like this:
[Test]
public void GatewayUserCascadesStudents()
{
var u = new GatewayUser() { FirstName = "Mama", LastName = "Bear", UserName = "somebody#example.com" };
var s1 = new Student() { FirstName = "First", LastName = "Student" };
var s2 = new Student() { FirstName = "Second", LastName = "Student" };
u.AddStudent(s1).AddStudent(s2);
using (var s = NewSession())
using (var tx = s.BeginTransaction())
{
s.Save(u);
tx.Commit();
}
GatewayUser fetched = null;
int count = 0;
using (var s = NewSession())
{
fetched = s.Get<GatewayUser>(u.Id);
count = fetched.Students.Count;
}
Assert.AreEqual(2, count);
}
The generated SQL inserts into both AspNetUsers and GatewayUser (reflecting the inheritance relationship), and inserts two records into Student. All good. On fetching, the SELECT joins the two user tables, and I get a GatewayUser object, but accessing the Students collection does not trigger a SELECT on the Student table. But if I change the mapping to Lazy(CollectionLazy.NoLazy), the SQL to select eagerly load Students appears in the log, but the collection is not populated. If I switch the database from SQLite to Sql Server, I see the student records in the table. The generated SQL (when NoLazy is applied) will fetch them. So on the database end, things look fine.
I have to think my Frankenstein mapping situation is to blame. I'm mixing the library's conformist mapping with Fluent mapping, and there are two different base entity classes. However, the generated schema looks correct, and the save cascades correctly, so I don't know if that's the issue.
Found my own answer. My mapping of the parent class's list was like this:
public class GatewayUserMap : JoinedSubclassMapping
{
public GatewayUserMap()
{
Key(g => g.Column("Id"));
Property(c => c.FirstName, m => m.Length(50));
// ... more properties
List(gu => gu.Students, map =>
{
map.Key(c => c.Column("GatewayUser_Id"));
map.Cascade(Cascade.All | Cascade.DeleteOrphans);
map.Index(li => li.Column("ListIndex"));
map.Access(Accessor.Field | Accessor.NoSetter);
}
);
}
}
I have a private backing field for the collection. Removing Accessor.NoSetter from the collection mapping fixed it. In fact, it still worked without Accessor.Field -- I guess the mapper does a good job of looking around for one, and using it if found. Changing the name of the private backer from "_students" to "funnyName" prevented the mapper from finding it.
How can I make LINQ query (I am using Entity Framework) that returns top n elements in a child collection?
Here are example classes
public class A {
public int ID
public ICollection<B> bData
}
public class B {
public int ID
public string Name
}
This is kind of query I was thinking;
db.A.Where(a => a.ID == query_id).Include(a => a.bData.Take(count)).ToList();
Normally this doesn't work, how could I accomplish this?
The Include method is intended for eager loading the related entity data. It's all or nothing and cannot be used for filtering, sorting, grouping etc.
So what are you asking for can be accomplished with projection (select). It would be simpler if you project to anonymous or custom type (a.k.a. DTO object), because EF does not allow projecting to entity type. But it's still doable by using anonymous type projection in LINQ to Entities query and doing second projection in LINQ to Objects to attach the filtered child collection to its parent entity like this:
var result = db.A
.Where(a => a.ID == query_id)
.Select(a => new { a, bData = a.bData.Take(count).ToList() })
.AsEnumerable() // Switch to LINQ to Object context
.Select(x =>
{
x.a.bData = x.bData;
return x.a;
})
.ToList();
Please note that in order the above to work, bData member should not be virtual or db.Configuration.LazyLoadingEnabled should be false (i.e. lazy loading should be off, otherwise the bData collections will be fully reloaded when first accessed).
I have the following objects:
Parent
public virtual Guid Id { get; set; }
public virtual DateTime TimeStamp { get; set; }
public virtual IList<Child> Childs { get; set; }
Child
public virtual Guid Id { get; set; }
public virtual string Name { get; set; }
I use Fluent to Map One To Many as follows:
.Override<Parent>(obj =>obj.HasMany(x => x.Childs)
.Cascade.All()
.Not.Inverse()
.Not.KeyNullable()
.Not.KeyUpdate())
I need to get up to all Parent with Childs between dates order by TimeStamp.
I am trying to do it as follows (maxCapacity is int):
QueryOver<Parent>().Where(x => x.TimeStamp > from)
.And(x => x.TimeStamp < to).OrderBy(x => x.TimeStamp).Desc
.Left.JoinQueryOver<Child>(x => x.Childs)
.TransformUsing(new DistinctRootEntityResultTransformer())
.Take(maxCapacity).List();
The result is not what I expected since the Take(maxCapacity) is not on the parent result but on the total query result which includes parent and child.
How can I get the latest X already transformed Parent rows?
Thanks.
The way I would go here is:
load the list of root entity (Parent) and
let NHibernate load their children lazily - in separated SQL query.
To avoid 1 + N issue, we can use smart mapping feature:
19.1.5. Using batch fetching
NHibernate can make efficient use of batch fetching, that is, NHibernate can load several uninitialized proxies if one proxy is accessed (or collections. Batch fetching is an optimization of the lazy select fetching strategy. There are two ways you can tune batch fetching: on the class and the collection level.
Batch fetching for classes/entities is easier to understand. Imagine you have the following situation at runtime: You have 25 Cat instances loaded in an ISession, each Cat has a reference to its Owner, a Person. The Person class is mapped with a proxy, lazy="true". If you now iterate through all cats and call cat.Owner on each, NHibernate will by default execute 25 SELECT statements, to retrieve the proxied owners...
So, the query should be like this:
session.QueryOver<Parent>()
.Where(x => x.TimeStamp > from)
.And(x => x.TimeStamp < to).OrderBy(x => x.TimeStamp).Desc
//.Left.JoinQueryOver<Child>(x => x.Childs)
// .TransformUsing(new DistinctRootEntityResultTransformer())
.Skip(start) // paging
.Take(maxCapacity)
.List<Parent>();
And the parent mapping should be like:
<class name="Parent">
...
<bag name="Childs" batch-size="3">
...
</bag>
</class>
Please, check also these:
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
When I have an entity object with a one-to-many child collection, and I need to query for a specific child object, is there a feature or some clever pattern I haven't come up with yet to avoid that NHibernate fetches the entire child collection?
Example:
class Parent
{
public virtual int Id { get; proteced set; } // generated PK
public virtual IEnumerable<Child> Children { get; proteced set; }
}
class Child
{
public virtual int Id { get; protected set; } // generated PK
public virtual string Name { get; protected set; }
public virtual Parent Parent { get; protected set; }
}
// mapped with Fluent
class Service
{
private readonly ISessionFactory sessionFactory;
public Service(ISessionFactory sessionFactory)
{
this.sessionFactory = sessionFactory;
}
void DoSomethingWithChildrenNamedBob(int parentId)
{
using(var session = sessionFactory.OpenSession())
{
var parent = session.Get<Parent>(parentId);
// Will cause lazy fetch of all children!
var childrenNamedBob = parent.Children.Where(c => c.Name == "Bob");
// do something with the children
}
}
}
I know it's not the best example because in this case one would probably just query the Child entities directly, but I have encountered situations where I already had a Parent object and needed to traverse specific sub-trees through it.
Short answer: no. Longer answer: you can make it do this, with some sleight of hand.
Rippo's answer above shows how you would do it the 'proper' NHibernate way (whether it's with Linq or QueryOver or HQL doesn't really matter - the point is you have to step outside the parent -> child relationship to do a query). You can take this a step further and disguise this behind a façade. But to do so, you have to remove the mapped relationship entirely and replace it with a query at all times. You'd take out the Parent -> Children mapping, but leave the Child -> Parent mapping intact; then re-write the property on Parent to look like this:
public virtual IQueryable<Child> Children
{
get
{
// somehow get a reference to the ISession (I use ambient context), then
return session.Query<Child>().Where(c => c.Parent == this);
}
}
Now, when you use Parent.Children you get back a queryable collection, so you could then write
IEnumerable<Child> childrenNamedBob = parent.Children.Where(c => c.Name == "Bob");
The only way you could do this and preserve the mapping is to amend NHibernate's collection objects (or inject your own). Diego Mijelshon (who is around these parts) wrote a spike of exactly that, adding IQueryable support to NHibernate collections so you could do
IEnumerable<Child> childrenNamedBob = parent.Children.AsQueryable().Where(c => c.Name == "Bob");
But from what I can see, this never went any further and there's no apparent plan to add this capability to NH. I have run Diego's code and it does work, but obviously it's not production quality and hasn't been tested, and I don't think it's ever been officially 'released' even as a private patch.
Here's the link to the discussion on the NH issue tracker: https://nhibernate.jira.com/browse/NH-2319
I believe NH should support this out of the box, as it's a natural way for most .NET devs to want to interact with pretty much anything enumerable, now that we have Linq, and not being able to do it without the side-effect of loading an unbounded collection into RAM sucks. But the traditional NH model is session -> query and that's what 99% of people use.
I asked the same question on NHusers a few weeks ago and didn't get an answer so I suspect the answer is you will always get all the parents children and then perform a in-memory filter. In many cases this might be the correct way in seeing it.
In your case I would rewrite the query to be:-
var childrenNamedBob = session.Query<Children>()
.Where(w => w.Parent.Id == parentId && w.Name == "Bob");
Then simply to get parent (if childrenNamedBob has results) you could call:-
var parent = childrenNamedBob.First().Parent;
or as you rightly pointed out:-
var parent = session.Get<Parent>(parentId);
You can now do that with NHibernate 5 directly without specific code !
See https://github.com/nhibernate/nhibernate-core/blob/master/releasenotes.txt
Build 5.0.0
=============================
** Highlights
...
* Entities collections can be queried with .AsQueryable() Linq extension without being fully loaded.
...
I'm having bit complicated object model that forms a triangle. There is User entity that has collections of Items and Taxonomies. Item has a taxonomy, too. And for convenience, I wanted Item and Taxonomy to know its owner and Taxonomy to know its Item, if any. See diagram:
So this makes three bi-directional relations. My problem is when I map it in NHibernate like that and asking for user with given ID, I'm getting Select N+1 problem.
At first, User is loaded with eagerly fetched Items. Then Taxonomies are loaded with eagerly fetched Item connected to it. And this is as expected and as defined in mappings. But now there is N+1 queries to load Items related with Taxonomies.
This is redundant as all parts of object graph was already loaded. Thie problem disappears when I make my User-Item relation unidirectional from User side (there are only 2 queries, as expected), but I don't want to remove that backward relationship. Is it possible to have optimal fetching with all three relations bidirectional?
Here are my mapping parts:
public class UserOverride : IAutoMappingOverride<User>
{
public void Override(AutoMapping<User> mapping)
{
mapping.HasMany(x => x.Items).Inverse()
.Not.LazyLoad().Fetch.Join();
mapping.HasMany(x => x.Taxonomies).Inverse()
.LazyLoad().Fetch.Select();
}
}
public class ItemOverride : IAutoMappingOverride<Item>
{
public void Override(AutoMapping<Item> mapping)
{
mapping.References(x => x.Taxonomy); // many-to-one
}
}
public class TaxonomyOverride : IAutoMappingOverride<Taxonomy>
{
public void Override(AutoMapping<Taxonomy> mapping)
{
mapping.HasOne(x => x.Item).PropertyRef(x => x.Taxonomy)
.Not.LazyLoad().Fetch.Join();
}
}
And I query my database the simplest possible way:
var user = session.Get<User>(1);
Because mappings will effect all queries, I like to live by the rule that mappings should only be changed to eagerly load if an entity is NEVER useful without an other entity. In your situation, if you ever just want Users, and could care less about the Item and the Taxonomy records, you will be doing extra database work for no benefit.
I would advise you perform the eager loading via the other route- in your query.
Session.QueryOver<User>().Where(u => u.Id == 1)
.join.QueryOver<Items>(u => u.Items)
.Join.QueryOver<Taxonomy>(i => i.Taxonomy)
.TransformUsing(Trasnformers.DistinctRootEntity);