Fluent nhibernate map multiple tables
There is no way how to generate such SELECT statement.
I would suggest to use batch fetching. See these for more details:
- How to Eager Load Associations without duplication in NHibernate?
- How to implement batch fetching with Fluent NHibernate when working with Oracle?
The adjusted mapping:
public UserEntityMap : ClassMap<UserEntity>
{
//Other properties
HasMany(x => x.Addresses)
.KeyColumn("User_id").Fetch.Join()
.BatchSize(100);
HasMany(x => x.Roles)
.KeyColumn("User_id").Fetch.Join()
.BatchSize(100);
}
This will allow to query the root entity and with just few SELECTS get also their collections (no 1 + N issue)
Also check What is the solution for the N+1 issue in hibernate?
NHibernate Filtered Child Collection Lazy Loaded even with eager fetch specified
I would like to share my approach, maybe not the answer...
I. avoid fetching one-to-many
(collections)
When creating any kind of complex queries (ICriteria, QueryOver) we should use (LEFT) JOIN only on a start schema. I.e. on many-to-one
(References()
in fluent). That leads to expected row count from the perspective of paging (there is always only ONE row per root Entity)
To avoid 1 + N issue with collections (but even with many-to-one in fact) we have the NHiberante powerful 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)...
Read more here:
- How to Eager Load Associations without duplication in NHibernate?
- How to implement batch fetching with Fluent NHibernate when working with Oracle?
So, in our case, we would adjust mapping like this:
public PricingIncrementMap()
: base("PricingIncrement")
{
Map(x => x.IncrementYear);
HasMany<OptionPrice>(x => x.OptionPrices)
.KeyColumn("OptionIdentifier_id")
.Cascade.None()
.Inverse() // I would use .Inverse() as well
// batch fetching
.BatchSize(100);
}
II. collection filtering
So, we managed to avoid 1 + N issue, and we also query only star schema. Now, how can we load just filtered set of items of our collection? Well, we have native and again very powerful NHibernate feature:
18.1. NHibernate filters.
NHibernate adds the ability to pre-define filter criteria and attach those filters at both a class and a collection level. A filter criteria is the ability to define a restriction clause very similiar to the existing "where" attribute available on the class and various collection elements...
Read more about it here:
- how to assign data layer-level filters
- Limit collection to retrieve only recent entries for readonly entity
So in our case we would define filter
public class CollFilter : FilterDefinition
{
public CollFilter()
{
WithName("CollFilter")
.WithCondition("PricingIncrement_id = :pricingIncrementId")
.AddParameter("pricingIncrementId",NHibernate.Int32);
}
}
And we would need to extend our mapping again:
HasMany<OptionPrice>(x => x.OptionPrices)
.KeyColumn("OptionIdentifier_id")
.Cascade.None()
.Inverse()
// batch fetching
.BatchSize(100)
// this filter could be turned on later
.ApplyFilter<CollFilter>();
Now, before our query will be executed, we just have to enable that filter and provide proper ID of the year 2015:
// the ID of the PricingIncrement with year 2015
var pricingIncrementId thes.Session
.QueryOver<PricingIncrement>()
.Where(x => x.IncrementYear == 2015)
.Take(1)
.Select(x => x.ID)
.SingleOrDefault<int?>();
this.Session
.EnableFilter("CollFilter")
.SetParameter("pricingIncrementId", pricingIncrementId);
// ... the star schema query could be executed here
III. Sub-query to filter root entity
Finally we can use sub-query, to restrict the amount of root entities to be returned with our query.
15.8. Detached queries and subqueries
Read more about it here:
- Query on HasMany reference
- NHibernate Criteria Where any element of list property is true
so, our subquery could be
// Subquery
var subquery = DetachedCriteria.For<OptionPrice >()
.CreateAlias("Increment", "i", JoinType.InnerJoin)
.Add(Restrictions.Eq("i.IncrementYear", 2015))
.SetProjection(Projections.Property("Option.ID"));
// root query, ready for paging, and still filtered as wanted
ICriteria pagedCriteria = this.Session.CreateCriteria<OptionIdentifier>()
.Add(Subqueries.PropertyIn("ID", subquery))
.SetResultTransformer(CriteriaSpecification.DistinctRootEntity);
Summary: We can use lot of features, which are shipped with NHibernate. They are there for a reason. And with them together we can achieve stable and solid code, which is ready for further extending (paging at the first place)
NOTE: maybe I made some typos... but the overall idea should be clear
How to make one to one mapping to respect parent/owner object's batching in Fluent nHibernate?
You are almost there. All the batch-size
mapping you've done is correct, but BatchSize could be set not only to the Collections, but to classes
as well.
public ActorMap()
{
Id(x => x.ActorId, "actor_id");
Map(x => x.FirstName, "first_name");
BatchSize(100);
}
NOTE: My experience is to use it almost for every class, exactly for a reason you mentioned here... We'll gain lazy loading with more effective batching
nhibernate QueryOver select one table after join
One way would be, to use Transformers.DistinctRootEntity
For example:
var query = Session.QueryOver(() => parentAlias)
.Left.JoinAlias(x => x.Children, () => childAlias)
.Where(whereClause)
// Here is the trick
.TransformUsing(Transformers.DistinctRootEntity)
See more in this Andrew Whitaker post:
QueryOver Series - Part 4: Transforming
The other way...
Well, I would not go, in fact I do go, do not use this approach. My really proved way is to:
- Create query from Child and join parent (star schema)
- If we have to query parent
- for filtering through child use subselects
- for loading children in batches use mapping setting
batch-size
The first is obvious, and because we query star schema, we can easily use paging
The second would need some adjustments for filtering parent by child:
- Fetching Paginated Entity with Collection
For effective loading of child collection, we can profit from batch-size mapping. Check these:
- How to Eager Load Associations without duplication in NHibernate?
- How to implement batch fetching with Fluent NHibernate when working with Oracle?
Related Topics
Newtonsoft Inline Formatting for Subelement While Serializing
How to Partially Project a Child Object with Many Fields in Nhibernate
The Bare Minimum Needed to Write a Msmq Sample Application
What's the Difference Between Utf8/Utf16 and Base64 in Terms of Encoding
Why Can't C# Use Inline Anonymous Lambdas or Delegates
How to Generate Random Color Names in C#
Split a String That Has White Spaces, Unless They Are Enclosed Within "Quotes"
Difference Between Select and Convertall in C#
Why Does System.Threading.Timer Stop on Its Own
How to Use Async to Increase Winforms Performance
Total Number of Items Defined in an Enum
Current Possibilities for Tracing Program Flow in C#