I have a parent/child relationship mapped with a many-to-many set.
public class Parent
{
public ISet<Child> Children { get; set; }
}
public class Child {}
public class ParentMap : ClassMap<Parent>
{
HasManyToMany(x => x.Children)
.AsSet();
}
How can I write a query to select all of the parents that contain a given child? I would have guessed that it would be something like this, but this API does not exist:
Session.CreateCriteria<Parent>()
.Add(Expression.Contains("Children", child)
.List<Parent>();
I can't for the life of me find the answer anywhere. My brain is not fully functioning today and Google has so far failed me.
How about something like this?
Session.CreateCriteria<Parent>()
.CreateCriteria("Children")
.Add(Expression.Eq("Id", child.Id)
.List<Parent>();
or
Session.CreateCriteria<Parent>()
.CreateCriteria("Children")
.Add(Expression.In("Id", child.Id)
.List<Parent>();
so you can pass in an array of Ids.
Related
I have a parent object with a child collection containing one element, the child collection contains a "grandchild" collection containing 3 elements.
I am loading the parent object from the database using NHibernate as follows
Parent parentObject = session.Query<Parent>()
.FetchMany(x => x.Children)
.ThenFetchMany(x => x.GrandChildren)
.Where(x => x.Id = "someparentid")
.Single();
What I'm finding is that there are duplicate children objects (3 in total) attached to the parent object when there should be only one. (There are 3 grandchild objects correctly attached to each child.) Eager loading the children collection only works correctly.
Do you know how I can achieve loading of the full parent object without duplicate children?
If you map Children and GrandChildren as set, you can avoid the cartesian product. You need to define Children and Grandchildren as collections:
public class Parent
{
...
public virtual ICollection<Child> Children { get; set; }
...
}
public class Child
{
...
public virtual ICollection<GrandChild> GrandChildren { get; set; }
...
}
And in the mapping (using FluentNHibernate):
public class ParentMapping : ClassMap<Parent>
{
public ParentMapping()
{
...
HasMany(x => x.Children)
.KeyColumn("ParentID")
.Inverse
.AsSet()
...
}
}
public class ChildMapping : ClassMap<Child>
{
public ChildMapping()
{
...
HasMany(x => x.GrandChildren)
.KeyColumn("ChildID")
.Inverse
.AsSet()
...
}
}
I was able to use the answer here using QueryOver, it correctly loads the objects while generating efficient SQL (selects per table instead of one huge join).
If you are using Linq, you can simplify it with this:
int parentId = 1;
var p1 = session.Query<Parent>().Where(x => x.ParentId == parentId);
p1
.FetchMany(x => x.Children)
.ToFuture();
sess.Query<Child>()
.Where(x => x.Parent.ParentId == parentId);
.FetchMany(x => x.GrandChildren)
.ToFuture();
Parent p = p1.ToFuture().Single();
Detailed explanation here: http://www.ienablemuch.com/2012/08/solving-nhibernate-thenfetchmany.html
You can't do it with NHibernate (I don't think you can do it with EF4 either) since your result is a Cartesian product. You're getting all results from all tables.
NHibernate doesn't know how to map the results from both collections back to the root. So for each Children, you get the same number of GrandChildren, and for each GrandChildren you end up with the same number of Children.
I have a scenario where I have a base class as one entity, then another entity that derives from the other base class. Both have meaning in my domain and can be used separately.
public class MyBaseClass
{
int ID { get; set; }
string Name { get; set; }
}
public class MyChildClass
{
string AdditionalField { get; set; }
}
I have both mapped using Fluent nHibernate using ClassMap like this:
public class MyBaseClassMap : ClassMap<MyBaseClass>
{
Id("MyBaseClassID");
Map(x => x.Name);
}
public class MyChildClassMap : SubclassMap<MyChildClass>
{
Map(x => x.AdditionalField);
}
What is happening is when I try to fetch a copy of the base class, its using the mapping for the child class. Its as if it doesn't know the the difference between the base and child class, or its choosing the wrong mapping for it. I confirmed this by watching the SQL statement and its joining to the child table and fetching the additional column. Any way to get it to use the right map?
That's the 'nature' of NHibernate.
The behaviour you're describing, is called 'polymorphic queries'.
Since MyChildClass is a MyBaseClass, the MyChildClass instances are retrieved as well.
If you want to avoid this behaviour, you can maybe have a look at the answers in this topic. (I've never 'disabled' the polymorphic query ability).
Disclaimer: I'm fairly new to NH & ORM in general.
Disclaimer: I'm working with a build of FNH from here in order to use with NH3.0GA.
The problem in a nutshell is that I would like to use FNH's SubclassMap as a way to map a LEFT JOIN, table-per-subclass scenario to my object hierarchy which is defined as:
public class MyBaseClass {
public virtual int Id { get; set; }
}
public class MySubClass : MyBaseClass {
public virtual string SubClassVal { get; set; }
}
This is mapped via FNH as:
public class MyBaseClassMap : ClassMap<MyBaseClass> {
public MyBaseClassMap() {
Table("BaseClass");
Id(x => x.Id, "Id").GeneratedBy.Assigned();
}
}
public class MySubClassMap : SubclassMap<MySubClass> {
public MySubClassMap() {
Table("SubClass");
KeyColumn("Id");
Map(x => x.SubClassVal);
}
}
And I retrieve via:
public class Repository {
ISession session; //assume properly initialized ISession
public IList<T> GetAll<T>() where T: class {
return session.CreateCriteria<T>().List<T>();
}
}
And in my database, I've got 1 record in my BaseClass table, 0 records in SubClass.
Now, what I would like to do is pull the entity out as a MySubClass instance by doing something like this:
var rep = new Repository();
var subclasses = rep.GetAll<MySubClass>();
And of course, there are no instances in the returned collection as this is presumably performing an INNER JOIN underneath it all. This is where I'm stuck. I've managed to discover that specifying an 'optional' join is what I'm supposed to do. I've attempted to modify MySubClassMap to:
public class MySubClassMap : SubclassMap<MySubClass> {
public MySubClassMap() {
Join("SubClass", j => {
j.KeyColumn("Id");
j.Optional();
j.Map(x => x.SubClassVal); // note that I've tried the map outside the Join() below, to no avail
});
//Map(x => x.SubClassVal);
}
}
Compiling/running this presents me with the following (innermost) exception:
The element 'joined-subclass' in namespace 'urn:nhibernate-mapping-2.2' has invalid child element 'join' in namespace 'urn:nhibernate-mapping-2.2'. List of possible elements expected: 'property, many-to-one, one-to-one, component, dynamic-component, properties, any, map, set, list, bag, idbag, array, primitive-array, joined-subclass, loader, sql-insert, sql-update, sql-delete, resultset, query, sql-query' in namespace 'urn:nhibernate-mapping-2.2'.
I'll save posting the stack trace, but the jist of it is:
MyApp -->
FluentNHibernate.Cfg.FluentConfiguration.BuildSessionFactory() -->
NHibernate.Cfg.FluentConfiguration.BuildConfiguration()
I think that's all the relevant info. I suspect I may be bumping into a breaking change between this very new version of NH and version of FNH that isn't so new. But, as mentioned earlier, I am a rookie, and could well be doing something stupid. If this is the case, I'd very much appreciate somebody smacking me over the head with what probably should be obvious.
Thanks in advance.
Entities have one type, which doesn't change. If you have a record in your BaseClass table only, that entity is and will always be a MyBaseClass.
If entities can change their "type", you shouldn't use inheritance but composition.
I'm having a problem getting FluentNHibernate's HasMany<> mapping to behave as I would expect.
I have a class hierarchy of Child is a descendant of Parent, defined using table-per-hierarchy, discriminated by the class name in a column named 'TYPE'. An unrelated class Group contains a collection of Child elements.
So, the Group class is defined as:
public class Group
{
public virtual IList<Child> Children {get; protected set;}
public Group()
{
Children = new List<Children>();
}
}
My map in Fluent NHibernate looks like this:
public class GroupMap : SubclassMap<Group>
{
public GroupMap()
{
HasMany<Child>(x => x.Children)
.Not.LazyLoad()
.Inverse()
.Cascade.All();
}
}
I would expect that using Group.Children would result in a collection of objects of type Child, but it is returning a collection that contains objects of either type Parent or Child.
Looking at the SQL generated, the select statement is not differentiating based on the TYPE column in the table that holds the Parent and Child objects.
Changing my mapping to:
public class GroupMap : SubclassMap<Group>
{
public GroupMap()
{
HasMany<Child>(x => x.Children)
.Inverse()
.Not.LazyLoad()
.Where("Type = 'Child'")
.Cascade.All();
}
}
solves the problem, but it seems like a hack to me. Shouldn't the declaration "HasMany' infer that the results should be only of type Child?
Am I mapping my collections incorrectly?
The short answer is I've moved away from using FluentNHibernate mappings.
I'm back to using plain .hbm.xml mappings and much happier as a result.
How can I map this:
public class Customer
{
private IList<Order> _orders;
public IEnumerable<Order>
GetAllOrders()
{
return _orders;
}
}
On the project page are some samples but none is about this situation.
There is this sample:
// model
public class Account
{
private IList<Customer> customers = new List<Customer>();
public IList<Customer> Customers
{
get { return customers; }
}
}
// mapping
HasMany(x => x.Customers)
.Access.AsCamelCaseField();
But it assumes that Account has public field Customers and that scenario is different as mine. I tried some possible options but none works:
HasMany(x => Reveal.Propertie("_orders"))
Private fields works fine in simple property mapping but collection mapping
is quite different. Any idea? Thanks
The easiest solution is to expose your collection as a public property Orders instead of the GetAllOrders() method. Then your mapping is
HasMany(x => x.Orders)
.Access.AsCamelCaseField(Prefix.Underscore);
and your class is
public class Customer
{
private IList<Order> _orders = new List<Order>();
public IEnumerable<Order> Orders
{
get { return _orders; }
}
}
If that doesn't work for you, it is possible to map private properties using Fluent NHibernate's Reveal mapping.
Edited to add: Having just done this, the correct answer is:
HasMany<Order>(Reveal.Property<Customer>("_orders")) etc.
The collection must be exposed as a protected virtual property to allow proxying:
protected virtual IList<Order> _orders { get; set; }
This answer put me on the right track.
Thanks.
Your solution is fine. However, there could be situations(hypotetical) when you dont want to reveal your private collection. This mapping scenario is not explained in your linked post because there is difference between mapping simple propertie as descibed in that post and collection mapping. My attempt to use HasMany(x => Reveal.Propertie("_orders")) failed because of raised exception.
You can map a completely private collection using Reveal.Member(), but it has a specific and non-obvious restriction: the Expression that HasMany() accepts has to return either IEnumerable<TReferenced> or object.
For your class:
public class Customer
{
private IList<Order> _orders;
public IEnumerable<Order> GetAllOrders()
{
return _orders;
}
}
the following line will populate the _orders collection:
HasMany(Reveal.Member<Customer, IEnumerable<Order>>("_orders"));
//additional mapping behaviors
For completeness - the following line gives a compiler error:
HasMany(Reveal.Member<Customer, IList<Order>>("_orders"));