HasMany<MySubtype> mapping returns supertype and all descendants? - fluent-nhibernate

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.

Related

Nhibernate Bidirectional One-to-one (not HasOne)

I'm trying to establish a One-to-One relationship (Two-sided References relationship) between two classes. Both Properties should not be nullable. The problem is when you try to save one first over the other, I encounter the Null or Transient value error.
class A
{
C C {get;set;}
}
class C
{
A A {get;set;}
}
class AMapping : ClassMap<A>
{
AMapping()
{
References(x=>x.C)
.Not.Nullable();
}
}
class CMapping : ClassMap<C>
{
CMapping()
{
References(x=>x.A)
.Not.Nullable();
}
}
I understand that since NHibernate can't make a reference to an object that doesn't exist (in the databaase) yet, but would there be a pattern or a feature that I can use to circumvent this limitation?
Try setting cascade="none" on one side of the Many-To-One mapping. Something like:
References(x=>x.C)
.Not.Nullable()
.Cascade.None();

how to achieve table per concrete class when base class is abstract in fluent nhibernate?

i have the following scenario
public abstract class BaseClass
{
public virtual int Id {get; set};
public virtual string Name {get; set;}
}
public class FirstSubClass : BaseClass
{
//properties and behaviour here
}
public class SecondSubClass : BaseClass
{
//properties of SecondSubclass Here
}
public class ProcessStep
{
public virtual IList<BaseClass> ContentElements {get; set;}
}
for mapping i have used following code snippet :-
this._sessionFactory =
Fluently.Configure().Database(SQLiteConfiguration.Standard
.ConnectionString(#"Data Source=SqliteTestSqlDataAccess.s3db; Version=3; New=True; Pooling=True; Max Pool Size=1;"))
.Mappings(m => m.AutoMappings.Add(AutoMap.Assembly(assemblyWithDomainClasses).Conventions.Add(DefaultCascade.All())))
.ExposeConfiguration(BuildSchema)
.BuildSessionFactory();
By default fluent will ignore the abstract base class that is BaseClass.
But as in the class ProcessStep there is property ContentElements which returns IList , i am getting an exception:-
NHibernate.MappingException : Association references unmapped class: BaseClass
If i include the base class using the IncludeBase(typeof(BaseClass)) then it works fine but it creates a table for BaseClass and Derived classes and the records are linked with FK-PK relationship(table per subclass).
What i want to achieve is table per concrete class. that is each derive class will have it's own table where there will all properties of derived class + properties in the base class.
Any idea how to achieve it?
Since I haven't seen your mapping, let me provide mine. You could achieve this by doing like this
public class BaseClassMap:ClassMap<BaseClass>
{
public BaseClassMap()
{
/*
* Identity generator can't be native because subclass objects should be unique
* So use HiLo or Guid or other generators which will generate unique id on the child tables
*/
Id(x => x.Id).GeneratedBy.Guid();
Map(x => x.Name);
UseUnionSubclassForInheritanceMapping(); // This is important - uses union-subclass mappings for the derived classes
}
}
public class FirstSubClassMap : SubclassMap<FirstSubClass>
{
public FirstSubClassMap()
{
Table("FirstSubClassTable");
// Map properties for FirstSubClass
}
}
public class SecondSubClassMap : SubclassMap<SecondSubClass>
{
public SecondSubClassMap()
{
Table("SecondSubClassTable");
// Map properties for SecondSubClass
}
}
It caused me headache to implement the "Table per Concrete Class" inheritance strategy with an abstract base class with nhibernate automapping. But I think, I've finally found a solution and want to share it with you. I also think, it's not added to the automapping docs, because it's maybe considered as a "weak" database design.
First here are some resources I found about this topic:
https://www.codeproject.com/Articles/232034/Inheritance-mapping-strategies-in-Fluent-Nhibernat
Example implementation of inheritance strategies in fluent nhibernate (!automapping).
https://github.com/jagregory/fluent-nhibernate/wiki/Automapping-inheritance
Documentation of inheritance strategies in fluent nhibernate with automapping.
(can't add another link) https : // github . com /jagregory/fluent-nhibernate/pull/25/commits/2984c8c4e89aa4cec8625538f763c5931121a4e7
Bug-Fix Union-SubClass implementation (Table per Concrete Class)
These resources basically describe how you need to do it:
As you already mentioned fluent nhibernate ignores abstract base classes. So you need to add them explicitly.
// abstractBaseTypes is just a simple enumeration of base types
// model is the AutoPersistenceModel
abstractBaseTypes.ForEach(m => model = model.IncludeBase(m));
a) If you know the abstract base types at compile time you can use
//sets the union subclass strategy for the known base model
model.Override<SuperType>(m => m.UseUnionSubclassForInheritanceMapping()))
b) If you don't know the concrete types you can create a mapping override for each base type:
public class AbstractRightEntryMappingOverride : IAutoMappingOverride<AbstractRightEntry>
{
public void Override(AutoMapping<AbstractRightEntry> mapping)
{
mapping.UseUnionSubclassForInheritanceMapping();
}
}
// You need to tell nhibernate where to find the overriden mappings.
// You simply can add the assemblies again.
modelAssemblies.ForEach(a => model = model.UseOverridesFromAssembly(a));

Loading a base class through nhibernate incorrectly uses mappings from derived classes

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).

Optional SubclassMap (left join) with NHibernate 3.0 GA & FNH

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.

NHibernate Criteria Collection Contains

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.