Can NHibernate query for specific children without lazy loading the entire collection? - 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.
...

Related

How to handle DAL for multiple layers of 1 to many relationship?

I have the following problem:
I have an aggregate root with multiple layers of one to many relationship.
Root -> has many
Child -> has many
GrandChild
I have Controller\s that handle logic done on each layer of the aggregate root.
I do not know how to handle the data access layer.
Should i just create a repository for the aggregate root and all Child and GrandChild operations get handled through it or it would be fine to create a repository for every level ?
Furthermore the GrandChildren entities in my case occupy a lot of space (they contain text) , thus i will be using a document database - RavenDB.
public class Root
{
public int ID{get;set;}
public IEnumerable<Child>Children;
}
public class Child
{
public int ChildID{get;set;}
public IEnumerable<Child>GrandChildren; //occupy a loot of space !
}
public class GrandChild
{
public int GrandChildID{get;set;}
}
public interface IGenericRepository<T>
{
bool Add<T>(T newValue);
T Get<T>(int id);
IEnumerable<T> GetAll();
bool Delete(int id);
bool Update<T>(T value);
}
Controller
public class ParentController
{
IGenericRepository<Root> repo;
public IActionResult<Root> Get(int rootId)
{
return this.repo.Get(rootId);
}
}
public class ChildControiller_V1
{
IGenericRepository<Child>repo;
public IActionResult<Child> Get(int childid)
{
this.repo.Get(childid); //the id is unique
}
}
Access through root
public class RootRepository:IGenericRepository<Root>
{
/// implementations
public IGenericRepository<Child> GetChildRepository()
{
return //some implementation of IGenericRepository for Child
}
}
public class ChildController_V2
{
IGenericRepository<Root>repo;
public IActionResult<Child> Get(int rootId,int childid)
{
var root=this.repo.Get(rootId);
var childRepo=root.GetChildRepository();
var get= childRepo.Get(childId);
}
}
I hope you get the idea.For more layers i would do this all the way down.What would be a good approach considering the lowest entities occupy a lot of space compared to the others?
Update
The Root will have to support Create,Delete - not much happening here
The Child will have to support Create,Delete - (The focus will be on GET
, something like GET 5 children starting from index=10 here)
The Grandchildren will have to support full CRUD with a heavy intensive work on Update .Their table size of GrandChildren will be >>>>> all the others combined.Every Grandchild will have a plain text column.
When i say table or column i am referring to their equivalent in a typical SQ L database
From a (classic)DDD point of view, repositories return fully materialized aggregates, where aggregates represent consistency/transactional boundaries. Having children and grandchildren repositories means you give that up and with it a big benefit of DDD. That being said, you need to determine where your consistency boundaries are. Are there constraints between the entities? If not, they can be there own aggregates. Remember that aggregates don't appear in other aggregates and references from an entity should only go to an aggregate root, not some other entity in another aggregates' hierarchy.
I already mentioned a few other points that may be interesting in my answer here, specifically a direction if the data is too much.
https://stackoverflow.com/a/59189413/2613363
Lastly, I will say Raven has versions (etags). Use them if you end up updating many child items.

Eager load Records in Orchard CMS

I'm building an Orchard CMS module, where I want to eager load data, but can't work out how to do this.
For example a Client has many Events, so I have a ClientRecord & EventRecord for these:
public class ClientRecord {
private IList<EventRecord> _eventRecords;
public virtual int Id { get; set; }
public virtual string Company { get; set; }
public virtual IList<EventRecord> EventRecords {
get { return _eventRecords ?? (_eventRecords = new List<EventRecord>()); }
set { _eventRecords = value; }
}
}
WhenI load a Client in my ClientController
var clientRecord = _clientRepository.Get(id);
and then display the Events in my View
<ul>
#foreach (var eventRecord in Model.EventRecords) {
<li>
#eventRecord.Name (#eventRecord.StartDate.ToShortDateString())
</li>
}
</ul>
the Events are displayed and MiniProfiler shows a separate query to lazy-load the Events.
I've tried putting an [Aggregate] attribute on the EventRecords collection in the ClientRecord, but this didn't have any effect.
I'm not that familiar with NHibernate, so hopefully this is something simple, but how do I specify I want to eager load the EventRecords when the ClientRecord is retrieved?
[EDIT]
In Orchard CMS the NHibernate mappings are created for you, based on the convention that the class is called xxxRecord and there is a database table in place with the same name.
So you don't (as far as I know) have a mapping file that you can specify this in. If I'm right about that, then the question is whether there is any way to specify you want eager loading in the query you use to retrieve the Client (rather like Entity Framework's "include" method).
You must specify eager loading in your nHibernate mapping file, using Fluent nhibernate this would look (something) like this:
HasMany(x => x.EventRecords).KeyColumn("CompanyId").Not.LazyLoad().Fetch.Select();
The Fetch.Select() will execute a second select statement to load all of the related Event records.
The Not.LazyLoad() tells nHibernate to execute this second select immediately, if you remove this execution would be deferred until the collection is accessed.
In response to your comments you can specify eager loading in your query using fetch also (LINQ example shown)
NHSession.Query<ClientRecord>().Fetch(c => c.EventRecords).ToList();

How do you automap List<float> or float[] with Fluent NHibernate?

Having successfully gotten a sample program working, I'm now starting
to do Real Work with Fluent NHibernate - trying to use Automapping on my project's class
heirarchy.
It's a scientific instrumentation application, and the classes I'm
mapping have several properties that are arrays of floats e.g.
private float[] _rawY;
public virtual float[] RawY
{
get
{
return _rawY;
}
set
{
_rawY = value;
}
}
These arrays can contain a maximum of 500 values.
I didn't expect Automapping to work on arrays, but tried it anyway,
with some success at first. Each array was auto mapped to a BLOB
(using SQLite), which seemed like a viable solution.
The first problem came when I tried to call SaveOrUpdate on the
objects containing the arrays - I got "No persister for float[]"
exceptions.
So my next thought was to convert all my arrays into ILists e.g.
public virtual IList<float> RawY { get; set; }
But now I get:
NHibernate.MappingException: Association references unmapped class: System.Single
Since Automapping can deal with lists of complex objects, it never
occured to me it would not be able to map lists of basic types. But
after doing some Googling for a solution, this seems to be the case.
Some people seem to have solved the problem, but the sample code I
saw requires more knowledge of NHibernate than I have right now - I
didn't understand it.
Questions:
1. How can I make this work with Automapping?
2. Also, is it better to use arrays or lists for this application?
I can modify my app to use either if necessary (though I prefer
lists).
Edit:
I've studied the code in Mapping Collection of Strings, and I see there is test code in the source that sets up an IList of strings, e.g.
public virtual IList<string> ListOfSimpleChildren { get; set; }
[Test]
public void CanSetAsElement()
{
new MappingTester<OneToManyTarget>()
.ForMapping(m => m.HasMany(x => x.ListOfSimpleChildren).Element("columnName"))
.Element("class/bag/element").Exists();
}
so this must be possible using pure Automapping, but I've had zero luck getting anything to work, probably because I don't have the requisite knowlege of manually mapping with NHibernate.
Starting to think I'm going to have to hack this (by encoding the array of floats as a single string, or creating a class that contains a single float which I then aggregate into my lists), unless someone can tell me how to do it properly.
End Edit
Here's my CreateSessionFactory method, if that helps formulate a
reply...
private static ISessionFactory CreateSessionFactory()
{
ISessionFactory sessionFactory = null;
const string autoMapExportDir = "AutoMapExport";
if( !Directory.Exists(autoMapExportDir) )
Directory.CreateDirectory(autoMapExportDir);
try
{
var autoPersistenceModel =
AutoMap.AssemblyOf<DlsAppOverlordExportRunData>()
.Where(t => t.Namespace == "DlsAppAutomapped")
.Conventions.Add( DefaultCascade.All() )
;
sessionFactory = Fluently.Configure()
.Database(SQLiteConfiguration.Standard
.UsingFile(DbFile)
.ShowSql()
)
.Mappings(m => m.AutoMappings.Add(autoPersistenceModel)
.ExportTo(autoMapExportDir)
)
.ExposeConfiguration(BuildSchema)
.BuildSessionFactory()
;
}
catch (Exception e)
{
Debug.WriteLine(e);
}
return sessionFactory;
}
I would probably do a one to many relationship and make the list another table...
But maybe you need to rethink your object, is there also a RawX that you could compose into a RawPoint? This would make a table with 3 columns (ParentID, X, Y).
The discontinuity comes from wanting to map a List to a value that in an RDBMS won't go in a column very neatly. A table is really the method that they use to store Lists of data.
This is the whole point of using an ORM like NHibernate. When doing all the querying and SQL composition by hand in your application, adding a table had a high cost in maintenance and implementation. With NHibernate the cost is nearly 0, so take advantage of the strengths of the RDBMS and let NHibernate abstract the ugliness away.
I see your problem with mapping the array, try it with an override mapping first and see if it will work, then you could maybe create a convention override if you want the automap to work.
.Override<MyType>(map =>
{
map.HasMany(x => x.RawY).AsList();
})
Not sure if that will work, I need to get an nHibernate testing setup configured for this stuff.
Since I posted my question, the Fluent NHibernate team have fixed this problem.
You can now automap ILists of C# value types (strings, ints, floats, etc).
Just make sure you have a recent version of FNH.
Edit
I recently upgraded from FNH 1.0 to FNH 1.3.
This version will also automap arrays - float[], int[], etc.
Seems to map them as BLOBs. I assume this will be more efficient than ILists, but have not done any profiling to confirm.
I eventually got an override to work - see the end of the code listing. The key points are:
a new mapping class called DlsAppOverlordExportRunDataMap
the addition of a UseOverridesFromAssemblyOf clause in
CreateSessionFactory
Also, it turns out that (at least with v. 1.0.0.594) there is a very big gotcha with Automapping - the mapping class (e.g. DlsAppOverlordExportRunDataMap) cannot be in the same Namespace as the domain class (e.g. DlsAppOverlordExportRunData)!
Otherwise, NHibernate will throw "NHibernate.MappingException: (XmlDocument)(2,4): XML validation error: ..." , with absolutely no indication of what or where the real problem is.
This is probably a bug, and may be fixed in later versions of Fluent NHibernate.
namespace DlsAppAutomapped
{
public class DlsAppOverlordExportRunData
{
public virtual int Id { get; set; }
// Note: List<float> needs overrides in order to be mapped by NHibernate.
// See class DlsAppOverlordExportRunDataMap.
public virtual IList<float> RawY { get; set; }
}
}
namespace FrontEnd
{
// NEW - SET UP THE OVERRIDES
// Must be in different namespace from DlsAppOverlordExportRunData!!!
public class DlsAppOverlordExportRunDataMap : IAutoMappingOverride<DlsAppOverlordExportRunData>
{
public void Override(AutoMapping<DlsAppOverlordExportRunData> mapping)
{
// Creates table called "RawY", with primary key
// "DlsAppOverlordExportRunData_Id", and numeric column "Value"
mapping.HasMany(x => x.RawY)
.Element("Value");
}
}
}
private static ISessionFactory CreateSessionFactory()
{
ISessionFactory sessionFactory = null;
const string autoMapExportDir = "AutoMapExport";
if( !Directory.Exists(autoMapExportDir) )
Directory.CreateDirectory(autoMapExportDir);
try
{
var autoPersistenceModel =
AutoMap.AssemblyOf<DlsAppOverlordExportRunData>()
.Where(t => t.Namespace == "DlsAppAutomapped")
// NEW - USE THE OVERRIDES
.UseOverridesFromAssemblyOf<DlsAppOverlordExportRunData>()
.Conventions.Add( DefaultCascade.All() )
;
sessionFactory = Fluently.Configure()
.Database(SQLiteConfiguration.Standard
.UsingFile(DbFile)
.ShowSql()
)
.Mappings(m => m.AutoMappings.Add(autoPersistenceModel)
.ExportTo(autoMapExportDir)
)
.ExposeConfiguration(BuildSchema)
.BuildSessionFactory()
;
}
catch (Exception e)
{
Debug.WriteLine(e);
}
return sessionFactory;
}
Didn't get any answers here or on the Fluent NHibernate mailing list that actually worked, so here's what I did.
It smells like a horrible hack, but it works. (Whether it will scale up to large data sets remains to be seen).
First, I wrapped a float property (called Value) in a class:
// Hack - need to embed simple types in a class before NHibernate
// will map them
public class MappableFloat
{
public virtual int Id { get; private set; }
public virtual float Value { get; set; }
}
I then declare the properties in other classes that need to be Lists of floats e.g.
public virtual IList<MappableFloat> RawYMappable { get; set; }
NHibernate creates a single database table, with multiple foreign keys, e.g.
create table "MappableFloat" (
Id integer,
Value NUMERIC,
DlsAppOverlordExportRunData_Id INTEGER,
DlsAppOverlordExportData_Id INTEGER,
primary key (Id)
)

hydrate in nhibernate?

what is hydrate in nhibernate? I am not able to get my head around this term.
This was used, in a video, in context of hydrating the child table rows.
Please advise.
Thanks
AJ
You may wish to look at Build Your own DAL. It has a section on hydration that you probably will find interesting.
But hydration from a really simple view means means take from the raw persistent storage module and map into an object/a list of objects.
Update
Look at Understanding Lazy Loading Strategies or Lazy Loading - Eager Loading. I think this is what is happening for the hydration of the objects in the video you describe.
Example
This is a really simple hydration example (Not an NHibernate example)
Say we have run a query like: select name, breed from tblDogs and that has these results
K9, GermanShepard
IBeBlind, Labrador
SmallAnoyance, Shitsu
Trigger, GermanShepard
And say we have the following object:
public class Dog {
public string name { get; set; }
public string breed { get; set; }
}
Now we can write our own hydrator:
public List<Dog> Hydrate(results rs) {
List<Dog> dogs = new List<Dog>();
foreach(Record rec in rs) {
Dog d = new Dog();
d.name = rec["name"];
d.breed = rec["breed"];
dogs.Add(d);
}
return dogs;
}

NHibernate - Do I have to have a class to interface with a table?

I have a class called Entry. This class as a collection of strings called TopicsOfInterest. In my database, TopicsOfInterest is represented by a separate table since it is there is a one-to-many relationship between entries and their topics of interest. I'd like to use nhibernate to populate this collection, but since the table stores very little (only an entry id and a string), I was hoping I could somehow bypass the creation of a class to represent it and all that goes with (mappings, configuration, etc..)
Is this possible, and if so, how? I'm using Fluent Nhibernate, so something specific to that would be even more helpful.
public class Entry
{
private readonly IList<string> topicsOfInterest;
public Entry()
{
topicsOfInterest = new List<string>();
}
public virtual int Id { get; set; }
public virtual IEnumerable<string> TopicsOfInterest
{
get { return topicsOfInterest; }
}
}
public class EntryMapping : ClassMap<Entry>
{
public EntryMapping()
{
Id(entry => entry.Id);
HasMany(entry => entry.TopicsOfInterest)
.Table("TableName")
.AsList()
.Element("ColumnName")
.Cascade.All()
.Access.CamelCaseField();
}
}
I had a similar requirement to map a collection of floats.
I'm using Automapping to generate my entire relational model - you imply that you already have some tables, so this may not apply, unless you choose to switch to an Automapping approach.
Turns out that NHibernate will NOT Automap collections of basic types - you need an override.
See my answer to my own question How do you automap List or float[] with Fluent NHibernate?.
I've provided a lot of sample code - you should be able to substitute "string" for "float", and get it working. Note the gotchas in the explanatory text.