Two collection of the Table per class hierarchy in the one class in the NHibernate/Conform - nhibernate

I faced with following case:
I have table per class hierarchy:
public abstract class Parent : BaseEntity, IHierarchyEntity
{
}
public class ChildA : Parent
{
public virtual string Name { get; set; }
}
public class ChildB : Parent
{
public virtual string Value { get; set; }
}
public class Container : BaseEntity
{
public Container()
{
CollectionOne = new HashSet<ChildA>();
CollectionTwo = new HashSet<ChildB>();
}
public virtual ICollection<ChildA> CollectionOne { get; set; }
public virtual ICollection<ChildB> CollectionTwo { get; set; }
}
Small piece of Domain mapper logic (it`s almost the same):
IEnumerable<Type> allPersistEntities = GetDomainEntities();
IEnumerable<Type> roots = allPersistEntities.Where(t => t.IsAbstract && t.InheritedFromBaseEntity());
IEnumerable<Type> hierarchyEntities = allPersistEntities.Where(t => typeof(IHierarchyEntity).IsAssignableFrom(t));
var hierarchyRoots = hierarchyEntities.Where(t => t.IsAbstract && t.InheritedFromBaseEntity());
orm.TablePerClassHierarchy(hierarchyRoots);
When I saved items everything is ok, but when I tried to get ones I get two of them in the CollectionOne (ChildA type) and error in the second one:
illegal access to loading collection
What I see in the sql:
NHibernate:
SELECT
container0_.Id as Id0_0_
FROM
CONTAINERS container0_
WHERE
container0_.Id=#p0;
#p0 = 1 [Type: Int32 (0)] NHibernate:
SELECT
collection0_.ContainerId as Containe5_1_,
collection0_.Id as Id1_,
collection0_.Id as Id1_0_,
collection0_.Name as Name1_0_
FROM
PARENTS collection0_
WHERE
collection0_.ContainerId=#p0;
#p0 = 1 [Type: Int32 (0)] NHibernate:
SELECT
collection0_.ContainerId as Containe5_1_,
collection0_.Id as Id1_,
collection0_.Id as Id1_0_,
collection0_.[Value] as Value3_1_0_
FROM
PARENTS collection0_
WHERE
collection0_.ContainerId=#p0;
#p0 = 1 [Type: Int32 (0)]
There is no discriminator field. Is it possible to fix it?

Can you try:
var hierarchyRoots = hierarchyEntities.Except(roots);
I think you might need to give it the exact leafs to map, I don't think it'll assume it should map all classes inheriting from the root as tpch.

Related

Fluent Nhibernate generating incorrect query resulting in Cartesian product instead of single row

After trying numerous things, I am still unable to figure out the right way to query the following relationship using Fluent NHibernate.
This would otherwise have been an easier task if I had to write SQL queries. Hope to get some better advice to avoid N+1 issue and unoptimized auto-generated SQL queries.
I have the following relationship which goes something as below :
Tags can have media content (Images of various predefined sizes, videos, documents etc.) associated with it.
- 1 Tags can have multiple Media Items mapped to it (Lets's say images with dimension 32x32, 64x64, 600x100, 0 or more Videos)
- Every media item is mapped to a media description which helps in identifying the size and type of the media
- The same media item can be used by a different tag. Example, having a generic image for all tags which do not have any icons.
Entities:
Media
public class Media:IEntity
{
private ICollection<TagMedia> _tagMedia;
public virtual int Id { get; set; }
public virtual string FilePath { get; set; }
public virtual MediaType MediaType { get; set; }
public virtual ICollection<TagMedia> TagMedia
{
get { return _tagMedia?? (_tagMedia= new List<TagMedia>()); }
protected set { _tagMedia= value; }
}
}
Tag
public class Tag:IEntity
{
private ICollection<TagMedia> _tagMedia;
public virtual int Id { get; set; }
public virtual string Name { get; set; }
public virtual ICollection<TagMedia> TagMedia
{
get { return _tagMedia?? (_tagMedia= new List<TagMedia>()); }
set { _tagMedia= value; }
}
}
TagMedia
public class TagMedia :IEntity
{
public virtual int Id { get; set; }
public virtual Media Media { get; set; }
public virtual Tag Tag { get; set; }
public virtual DateTime AddedOn { get; set; }
}
MediaType
public class MediaType:IEntity
{
public virtual int Id { get; set; }
public virtual string Description { get; set; }
}
Mappings
MediaMapping
public class MediaMapping : IAutoMappingOverride<Media>
{
public void Override(AutoMapping<Media> mapping)
{
mapping.Map(c => c.FileName).CustomSqlType("varchar(60)").Not.Nullable();
}
}
TagMapping
public class TagMapping : IAutoMappingOverride<Tag>
{
public void Override(AutoMapping<Tag> mapping)
{
mapping.HasMany<TagMedia>(c => c.TagMedia)
.KeyColumn("TagId")
.Cascade.SaveUpdate()
.BatchSize(25);
mapping.BatchSize(25);
mapping.DynamicUpdate();
mapping.DynamicInsert();
}
}
TagMediaMapping
public class TagMediaMapping : IAutoMappingOverride<TagMedia>
{
public void Override(AutoMapping<TagMedia> mapping)
{
mapping.Map(c=>c.AddedOn);
}
}
Query:
The following query gets the cartesian product of all media mapped to the tag and does not eliminate records other than "Icon-16x16". I expect the ORM to return no more than one row.
Any help would be highly appreciated.
_session.Query<Tag>()
.FetchMany(x => x.TagMedia)
.ThenFetch(x => x.Media)
.ThenFetch(x=>x.MediaType)
.Where(c => c.Id == id
&& c.TaxonomyMedia.Any(x=>x.Media.MediaType.Description== "Icon-16x16"))
.SingleOrDefault();
Generated SQL:
exec sp_executesql N'select *
from [Tag] Tag0_ left outer join [TagMedia] Tagme1_ on Tag0_.TagId=Tagme1_.TagId
left outer join [Media] media2_ on Tagme1_.MediaId=media2_.MediaId
left outer join [MediaType] mediatype3_ on media2_.MediaTypeId=mediatype3_.MediaTypeId
where Tag0_.TagId=#p0
and (exists (select Tagme4_.TagMediaId
from [TagMedia] Tagme4_ inner join [Media] media5_ on Tagme4_.MediaId=media5_.MediaId inner join [MediaType] mediatype6_ on media5_.MediaTypeId=mediatype6_.MediaTypeId where Tag0_.TagId=Tagme4_.TagId and mediatype6_.MediaTypeDescription=#p1))',N'#p0 int,#p1 nvarchar(4000)',#p0=102,#p1=N'Icon-16x16'
go
the SingleOrDefault function is a LINQ function and not implemented by NHibernate. Use .Take(1)
I think the solution is:
_session.Query<Tag>()
.FetchMany(x => x.TagMedia)
.ThenFetch(x => x.Media)
.ThenFetch(x=>x.MediaType)
.Where(c => c.Id == id
&& c.TaxonomyMedia.Any(x=>x.Media.MediaType.Description== "Icon-16x16"))
.Take(1)
.SingleOrDefault();

NHibernate: Recursive queries

I've got a basic tree structure that is stored in a single table. Let's say this is my model:
public class TreeNode {
public virtual Guid Id { get; private set; }
public virtual string Name { get; private set; }
public virtual IEnumerable<TreeNode> Contents { get; private set; }
}
and the table:
TREE_NODES
PK_NODE Guid
FK_NODE_PARENT Guid
NODE_NAME Varchar
I want the following implementation where the return value is a TreeNode with the full eagerly loaded tree of its children and their children, etc.
public class Tree {
ISessionFactory _sessions;
public TreeNode GetBy(Guid id) {
using(var s = _sessions.OpenSession())
return s.Linq<TreeNode>().Single(n => n.Id == id);
}
}
How would I do this mapping?
I doubt you can optimize it - there is no recurtion in basic SQL. You can optimize it using server-side procedures (server specific - some servers, like MySQL does not support them) but it still be doubtful as you get non-recursive components.
Probably the best way is to walk down the tree in loading function and force the evaluation. Something like:
public class TreeNode {
public virtual Guid Id { get; private set; }
public virtual string Name { get; private set; }
public virtual IEnumerable<TreeNode> Contents { get; private set; }
}
public class Tree {
ISessionFactory _sessions;
public TreeNode GetBy(Guid id) {
using(var s = _sessions.OpenSession()) {
return LoadSubTree(s.Linq<TreeNode>().Single(n => n.Id == id));
}
}
private LoadSubTree(TreeNode node) {
foreach(var n in node.Contents)
LoadSubTree(n);
}
}
PS. Tree is probably not the best place for ISessionFactory.

Eager Loading Using Fluent NHibernate/Nhibernate & Automapping

I have a requirement to load a complex object called Node...well its not that complex...it looks like follows:-
A Node has a reference to EntityType which has a one to many with Property which in turn has a one to many with PorpertyListValue
public class Node
{
public virtual int Id
{
get;
set;
}
public virtual string Name
{
get;
set;
}
public virtual EntityType Etype
{
get;
set;
}
}
public class EntityType
{
public virtual int Id
{
get;
set;
}
public virtual string Name
{
get;
set;
}
public virtual IList<Property> Properties
{
get;
protected set;
}
public EntityType()
{
Properties = new List<Property>();
}
}
public class Property
{
public virtual int Id
{
get;
set;
}
public virtual string Name
{
get;
set;
}
public virtual EntityType EntityType
{
get;
set;
}
public virtual IList<PropertyListValue> ListValues
{
get;
protected set;
}
public virtual string DefaultValue
{
get;
set;
}
public Property()
{
ListValues = new List<PropertyListValue>();
}
}
public class PropertyListValue
{
public virtual int Id
{
get;
set;
}
public virtual Property Property
{
get;
set;
}
public virtual string Value
{
get;
set;
}
protected PropertyListValue()
{
}
}
What I a trying to do is load the Node object with all the child objects all at once. No Lazy load. The reason is I have thousands of Node objects in the database and I have to send them over the wire using WCF Service.I ran into the classes SQL N+ 1 problem. I am using Fluent Nhibernate with Automapping and NHibernate Profiler suggested me to use FetchMode.Eager to load the whole objects at once. I am using the following qyuery
Session.CreateCriteria(typeof (Node))
.SetFetchMode( "Etype", FetchMode.Join )
.SetFetchMode( "Etype.Properties", FetchMode.Join )
.SetFetchMode( "Etype.Properties.ListValues", FetchMode.Join )
OR using NHibernate LINQ
Session.Linq<NodeType>()
.Expand( "Etype")
.Expand( "Etype.Properties" )
.Expand( "Etype.Properties.ListValues" )
When I run any of the above query, they both generate one same single query with all the left outer joins, which is what I need. However, for some reason the return IList from the query is not being loaded property into the objects. Infact the returned Nodes count is equal to the number of rows of the query, so the Nodes objects are repeated.Moreover, the properties within each Node are repeated, and so do the Listvalues.
So I would like to know how to modify the above query to return all unique Nodes with the properties and list values within them.
each mapping has to have lazy loading off
in Node Map:
Map(x => x.EntityType).Not.LazyLoad();
in EnityType Map:
Map(x => x.Properties).Not.LazyLoad();
and so on...
Also, see NHibernate Eager loading multi-level child objects for one time eager loading
Added:
Additional info on Sql N+1:
http://nhprof.com/Learn/Alerts/SelectNPlusOne
I figure it out myself. The key is to use SetResultTransformer() passing an object of DistinctRootEntityResultTransformer as a parameter. So the query now looks like as follows
Session.CreateCriteria(typeof (Node))
.SetFetchMode( "Etype", FetchMode.Join )
.SetFetchMode( "Etype.Properties", FetchMode.Join )
.SetFetchMode( "Etype.Properties.ListValues", FetchMode.Join )
.SetResultTransformer(new DistinctRootEntityResultTransformer());
I found the answer to my questions through these links:
http://www.mailinglistarchive.com/html/nhusers#googlegroups.com/2010-05/msg00512.html
http://ayende.com/Blog/archive/2010/01/16/eagerly-loading-entity-associations-efficiently-with-nhibernate.aspx
I ended up with something like this:
HasMany(x => x.YourList).KeyColumn("ColumnName").Inverse().Not.LazyLoad().Fetch.Join()
Just make sure to select your entity like this, to avoid duplication due to the join:
session.CreateCriteria(typeof(T)).SetResultTransformer(Transformers.DistinctRootEntity).List<T>();
SetResultTransformer with DistinctRootEntityResultTransformer will only work for Main object but IList collections will be multiplied.

nHibernate Criteria for selecting a parent if a child in a collection has a specific value

If I have the following class structure what is the NHibernate criteria to select a parent if one of it's children has a specific name?
public class Child
{
public int Id { get; set; }
public int Name { get; set; }
}
public class Parent
{
public int Id { get; set; }
public IList<Child> Children { get; set; }
}
I'd just create an alias to the collection and add restrictions.
var parentsWithKidName = session.CreateCriteria<Parent>()
.CreateAlias("Children", "c", JoinType.InnerJoin)
.Add(Restrictions.Eq("c.Name", childName))
.SetResultTransformer(Transformers.DistinctRootEntity())
.List<Parent>();
This would result in
select p.*
from parent p
inner join child c on /* however it's mapped? */
where c.Name = ?
The distinct root entity transformer will process the result set and remove duplicated parents. They still come across the wire though.
What I did was to create a criteria query for the parent type, use the return to create a criteria query for the child type, and then add the specific conditions to the child type sub query.
public virtual IList<T> GetByChildCriteria(string childName,
params ICriterion[] criterion)
{
ICriteria criteria = NHibernateSession
.CreateCriteria(persitentType)
.CreateCriteria(childName);
foreach (ICriterion criterium in criterion)
{
criteria.Add(criterium);
}
return criteria.List<T>();
}
Note: The NHibernateSession variable is of type ISession.

Nhibernate: Component with HasMany

I have this mapping:
public sealed class EntityMap : ClassMap<Entity>
{
public EntityMap ()
{
...
Component(entity => entity.StateHistory,
m => m.HasMany<HistoryItem<EntityState>>
(Reveal.Property<EntityStateHistory>("Items"))
.Table("EntityStateHistory")
.KeyColumn("IDEntity")
.Component
( m2 =>
{
m2.Map(esh => esh.Item, "State")
.CustomType(typeof(EntityState)).Not.Nullable();
m2.Map(esh=> esh.Date, "TransitionDate").Not.Nullable();
}
)
.Cascade.AllDeleteOrphan());
...
}
}
I want to make a query where i get a specific date (TransitionDate) in an EntityStateHistory entry.
If it was possible it would be something like this:
"select e from Entity e where e.StateHistory.Items.Date = :date"
but i can't do this, i don't know how i can access an History record date, knowing that History is a component that has itself a collection of components, and i need to access one of the properties of those components in the collection.
The object model is something like this:
public class Entity
{
private int ID {get; set;}
etc
...
public virtual EntityStateHistory StateHistory{ get; private set; }
}
public class EntityStateHistory: History<EntityState>
{
//some wraped properties and methods
public IList<HistoryItem<EntityState>> StateRecords
{
get { return base.Items;}
}
public bool ContainsStateRecord(EstadoOT state)
{
return base.Items.Count(i => i.Item.Equals(state)) > 0;
}
etc ...
}
public class History<T>
{
protected virtual IList<HistoryItem<T>> Items { get; private set; }
public History()
{
Items = new List<HistoryItem<T>>();
}
protected virtual HistoryItem<T> AddHistoryItem(DateTime data, T item)
{
...
}
}
public class ItemHistory<T>
{
#region NHibernate
private int ID { get; set; }
#endregion
public virtual DateTime Date { get; private set; }
public virtual T Item { get; private set; }
...
}
I know i will probably have to change the mapping for entity, and create a map for EntityStateHistory, but i would like to avoid that, because that means one more table. The way i have it is the most canonical mapping because has no need to map HistoryItem or EntityStateHistory, that means i only use one table to map EntityStateHistory:
Table EntitiStateHistory:
-IDEntity
-TransitionDate
-State
So is it possible with the current mapping to query the database for a Entity that has a specific history record date?
Thanks
It was so easy...
The problem was in the path:
"select e from Entity e where e.StateHistory.Items.Date = :date"
if i do this:
"select e from Entity e join e.StateHistory.Items i where i.Date = :date"
it works