I have the following domain model to map using ClassMaps/SubclassMaps
A - HasMany -> B (A should be table)
B is abstract in code (and should not be a table)
C,D,E inherits B and all have very different properties. Each of them should be a table with foreign key to A.
I get the tables I want, but I cannot see how to map HasMany from entity A that has one property IList<B> SomeProperty
I would like to define in ClassMap<A> that SomeProperty cascades on C,D,E
This obviously doesn't work, in ClassMap<A>:
HasMany<C>(x => x.B).Cascade.All();
HasMany<D>(x => x.B).Cascade.All();
HasMany<E>(x => x.B).Cascade.All();
As I cannot duplicate B.
Example:
public class Person
{
public virtual IList<Animal> Animals { get; set; }
public void AddAnimal(Animal animal)
{
Animals.Add(animal);
}
}
public abstract class Animal
{
//some common properties
}
public class Cat : Animal
{
//some cat properties
}
public class Horse : Animal
{
//some horse properties
}
In this case I would like ClassMap<Person> to map to Cat and Horse over the abstract class Animal.
Try...
public void AddAnimal(Animal animal)
{
animal.Person = this; //Recomended
Animals.Add(animal);
}
public abstract class Animal
{
public virtual Person Person {get;set;} //Recomended Addition
//some common properties
}
Related
I am using Fluent NHibernate and table per concrete class for inheritance mappings.
There is an abstract base class and two other subclasses.
My base class has Id column. Created tables are ok. All tables has its own Id column. But sequence is only one for these two tables.
I want to assign different sequence for every subclass.
public abstract class Base
{
public virtual int Id { get; set; }
}
public class BaseMap : ClassMap<Base>
{
public BaseMap()
{
Id(x => x.Id).GeneratedBy.Native();
}
}
public class A : Base
{
public virtual int AmountOfA { get; set; }
}
public class AMap : ClassMap<A>
{
public AMap()
{
Map(x => x.AmountOfA );
}
}
public class B : Base
{
public virtual int AmountOfB { get; set; }
}
public class BMap : ClassMap<B>
{
public BMap()
{
Map(x => x.AmountOfB );
}
}
Is this possible with Fluent NHibernate?
this is by design.
session.Get<Base>(1); would have undefined behavior because the id is not unique
consider the case when there is a reference to the base class Reference(x => x.SomeBase); with a the value 1 in the database: it would not be possible to know if A(Id: 1) or B(Id: 1) is the object referenced
if Base is only there to reuse the Id property then dont map it as own entity but create a base class
public class BaseMap<T> : ClassMap<T> where T : Base
{
public BaseMap()
{
Id(x => x.Id).GeneratedBy.Native();
}
}
public class AMap : BaseMap<A>
{
public AMap()
{
Map(x => x.AmountOfA );
}
}
Consider the following class hierarchy:
public abstract class Animal {}
public class Dog : Animal
{
public int DogTagNumber { get; set; }
}
public class Cat : Animal
{
public int CatTagNumber { get; set; }
}
Note: DogTagNumber and CatTagNumber is purposely placed in the subclasses instead of as TagNumber in Animal class to demonstrate property explicit to each subclass.
The question:
Using Fluent NHibernate, is it possible to map unique constrain on class discriminator together with subclasses's explicit properties, like below:
Unique("DOG", DogTagNumber) and also Unique("CAT", CatTagNumber)
The purpose is to ensure the uniqueness of each Dog and Cat with regard to the discriminator in the table on database level.
Thanks in advance.
try this one:
Map(x => x.Something).UniqueKey("KeyName");
DiscriminateSubClassesOnColumn("discr_column").UniqueKey("KeyName");
I have a mapping issue with the table-per-class hierarchy in Fluent/NHibernate. When retrieving the records from the database, I keep getting an error (the Wrong type exception)
Object with id: 2445763 was not of the specified subclass: ClassA (loading object was of wrong class [ClassB]) (record 2445763 does have the value "2" in the Type column)
In my domain, I have EntryBase, ClassA and ClassB. The classes are defined as
public abstract class EntryBase
{
public virtual int Id {get;set;}
public virtual string CommonProperty1 {get;set;}
*... (lots of other common properties)*
public virtual string CommonPropertyN {get;set;}
}
public class ClassA : EntryBase
{
public virutal string ClassAOnlyProperty {get;set;}
}
public class ClassB : EntryBase
{
public virutal string ClassBOnlyProperty {get;set;}
public virutal int ClassBOnlyOtherProperty {get;set;}
}
The mappings are:
public class EntryBaseMap : ClassMap<EntryBase>
{
public EntryBaseMap()
{
Table("MySingleTable");
Id(x => x.Id, "RecordId").GeneratedBy.Identity();
Map(x => x.CommonProperty1, "Field1Name");
...
Map(x => x.CommonPropertyN, "FieldNName");
DiscriminateSubClassesOnColumn<string>("Type");
}
}
public class ClassAMap : SubclassMap<ClassA>
{
public ClassAMap()
{
DiscriminatorValue("1");
Map(x => x.ClassAOnlyProperty);
}
}
public class ClassBMap : SubclassMap<ClassB>
{
public ClassBMap()
{
DiscriminatorValue("2");
Map(x => x.ClassBOnlyProperty);
Map(x => x.ClassBOnlyOtherProperty);
}
}
Any idea what might be amiss? I've correctly been able to store records of Class B, but when I retrieve them, it's attempting to load them as Class A. Is it a mapping problem?
As discussed in the comments, if you have collection properties meant to represent a single subclass, you need to add a where clause in the mapping:
.Where("Type = '1'")
Type is your discriminator column and 1 is the discriminator value that matches the type you're trying to load.
I have abstract class Vehicle and two classes that derive from: Car and ForkLift.
public abstract class Vehicle
{
public EngineBase Engine { get; set; }
}
public class Car : Vehicle
{
public GasEngine Engine { get; set; }
}
public class ForkLift : Vehicle
{
public ElectricEngine Engine { get; set; }
}
and Engine clasess:
public abstract class EngineBase
{
}
public class GasEngine : EngineBase
{
}
public class ElectricEngine : EngineBase
{
}
Engines are mapped with "table per class hierarchy". With Vehicles I want to use the same pattern.
How to map Engine class and derived with that Engine property?
How to do it with lazy-loading?
That code does not compile, which makes it improbable that you can map it.
Use a protected field in Vehicle and map it with an access strategy:
public abstract class Vehicle
{
protected Engine _engine;
}
In Fluent NHibernate this would be mapped:
References(x => x.Engine, "EngineId").Access.CamelCaseField(Prefix.Underscore);
Then the classes that extend Vehicle can cast it as needed:
public class Car : Vehicle
{
public GasEngine
{
get { return (GasEngine)_engine; }
set { _engine = Value; }
}
}
I'm in the process of adapting Fluent NHibernate to our existing legacy app and am trying to determine how to use ClassMap and SubclassMap for the entity hierarchy shown.
// BaseObject contains database columns common to every table
public class BaseObject
{
// does NOT contain database id column
public string CommonDbCol1 { get; set; }
public string CommonDbCol2 { get; set; }
// ...
}
public class Entity1 : BaseObject
{
public int Entity1Id { get; set; }
// other Entity1 properties
}
public class Entity2 : BaseObject
{
public int Entity2Id { get; set; }
// other Entity2 properties
}
The identity columns for Entity1 and Entity2 are uniquely named per table. BaseObject contains columns that are common to all of our entities. I am not using AutoMapping, and thought I could use ClassMap on the BaseObject, and then use SubclassMap on each Entity like this:
public class Entity1Map : SubclassMap<Entity1>
{
public Entity1Map()
{
Id(x => x.Entity1Id);
// ...
}
}
Problem is, Id() is not defined for SubclassMap. So, how do I specify within each Entity1Map, Entity2Map, ... (we have 100+ entity classes all inheriting from BaseObject) what the entity-specific Id is?
Thanks in advance for any insight!
It's not possible to do that in either Fluent NHibernate or NHibernate. Do you actualy want your classes to be mapped as subclasses, or do you just want them to share the common mappings? If you truly want subclasses, then you're going to need to have them share the identity column, no other way around it; if you don't want actual subclasses, create an abstract ClassMap<T> where T : BaseObject and map the common properties in there.
Something like:
public abstract class BaseObjectMap<T> : ClassMap<T> where T : BaseObject
{
public BaseObjectMap()
{
Map(x => x.CommonProperty1);
}
}