I have a Base class and two childs (A:Base and B:Base), and I want to map them to two tables (table A and table B). Is that possible in Fluent NHibernate? So I have:
public class Base
{
public virtual int Id {get;set;}
public virtual int IndexIn {get;set;}
public virtual Product Product {get;set;}
}
public class A : Base
{
public virtual string Value {get;set;}
}
public class B : Base
{
public virtual int Value {get;set;}
public virtual IList<Sequence> Sequences {get;set;}
}
My mapping is:
public class BaseMap : ClassMap<Base>
{
public BaseMap()
{
Id(x => x.Id);
Map(x => x.IndexIn);
References(x => x.Product);
}
}
public class AMap : SubclassMap<A>
{
public AMap()
{
Map(x => x.Value);
}
}
public class BMap : SubclassMap<B>
{
public BMap()
{
Map(x => x.Value);
HasMany(x => x.Sequences);
}
}
But in that case it creates three tables (A, B and Base). That's nice, but I need to reduce number of tables, so I am ok to have Base's fields in both A and B table. Generally I want to simply map A and B as normal classes (without using inheritance), but I need to be able to add some other class, where I can have property:
public virtual IList<Base> ListofAandB {get;set;}
If I remove BaseMap definition and just map A and B as ClassMap<> I receive error "Cannot find map definition for Base" if I try to use the property that is written above.
You won't be able to map your ListOfAAndB property if your inheritance is present only in your code and not in your database model. To have NHibernate map this property as it is defined, you are going to need a Base table in your database.
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 );
}
}
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.
Say I have two Entities.
public class Category{
public virtual int Id{get;set;}
public virtual IList<Post> Posts{get;set;}
}
public class Post{
public virtual int Id{get;set;}
public virtual string Title{get;set;}
}
In the Db there's a many-to-many table
CategoryPostRel
CategoryId
PostId
The category Map then looks like this:
public CategoryMap()
{
HasManyToMany(x => x.Posts)
.Table("CategoryPostRel")
.ParentKeyColumn("CategoryId")
.ChildKeyColumn("PostId");
}
Ok, but say I only want the Ids from the Posts. So I change my Category entity to look like this.
public class Category{
public virtual int Id{get;set;}
public virtual IList<int> PostIds{get;set;}
}
So now, how do I get the ids with my mapping as the HasManyToMany maps Entities, not columns right?
Note that I can't change the db at all and the many-to-many table has no unique identifier.
public CategoryMap()
{
HasManyToMany(x => x.PostIds)
.Table("CategoryPostRel")
.ParentKeyColumn("CategoryId")
.ChildKeyColumn("PostId").HowDoIgetTheIds...?
}
You could create an entity that models this relationship CategoryPost and do something like this:
public CategoryMap()
{
HasMany(x => x.CategoryPostIds)
.KeyColumn("CategoryId")
}
public CategoryPostMap
{
CompositeId()
.KeyProperty(x => x.PostId)
.KeyProperty(x => x.CategoryId)
}
This is obviously not an ideal solution but it may work.
I'm doing a very basic thing with Fluent NHibernate. I found a lot of people with similar problems here in SO but none seemed to fix my problem.
I have 1 Class like:
public abstract class ParentClass
{
public virtual long Id { get; private set; }
public virtual DateTime CreateDate { get; set; }
public virtual int Type { get; set; }
}
And 1 Concrete classes like:
public class ChildClass : ParentClass
{
public virtual string PropertyX { get; set; }
public virtual int PropertyY{ get; set; }
}
So I made the mapping as follows:
public class ParentMap : ClassMap<ParentClass>
{
public ParentMap()
{
Id(x => x.Id);
Map(x => x.CreateDate);
DiscriminateSubClassesOnColumn("Type");
}
}
And
public class ChildMap : SubclassMap<ChildClass>
{
public ChildMap()
{
Extends<ParentClass>();
DiscriminatorValue(1);
Map(x => x.PropertyX);
Map(x => x.PropertyY);
}
}
My legacy database has 2 tables, 1 that holds all the data from the ParentClass and another one that holds the data from the Child with a foreign key in the ID.
The idea is to have different tables for each different implementation of the ParentClass but having the ParentClass table as a single repository for "Ids" and "Create Dates".
I'm creating my SessionFactory as follows:
private static ISessionFactory CreateSessionFactory()
{
return Fluently.Configure()
.Database(MsSqlCeConfiguration.Standard.ConnectionString(cstr => cstr.FromConnectionStringWithKey("TheConnectionString")))
.Mappings(mappings => mappings.FluentMappings.AddFromAssemblyOf<ParentClass>()
.ExportTo(#"c:\temp\Mappings"))
.BuildSessionFactory();
}
I'm doing just a basic test of storing things to the database as:
public void Store(ParentClass parent)
{
using (var session = sessionFactory.OpenSession())
{
using (var transaction = session.BeginTransaction())
{
session.SaveOrUpdate(parent);
transaction.Commit();
}
}
}
But despite the method waits for a ParentClass variable, I'm passing a ChildClass instance for it (the method is actually a inheritance of an interface, that's why it expects a ParentClass).
And every time I it raises an error on "SaveOrUpdate" method saying "No persister for: ChildClass".
What am I doing wrong?
ps.: Another strange thing is that even with the "ExportTo" method on the SessionFactory creation, no mapping is being write on the folder.
Change
.Mappings(mappings => mappings.FluentMappings.AddFromAssemblyOf<ParentClass>()
To
.Mappings(mappings => mappings.FluentMappings.AddFromAssemblyOf<ParentMap>()
I have couple of classes and want to map them correctly to database:
public class A
{
public virtual Guid Id { get; private set; }
public virtual ComponentClass Component { get; set; }
}
public class ComponentClass
{
public virtual IList<B> Elements { get;set; }
}
public class B
{
public virtual Guid Id { get; private set; }
public virtual DateTime Time { get; set; }
}
I map them using fluent mappings like that:
public class AMap : ClassMap<A>
{
public A() {
Id(x => x.Id);
Component(x => x.Component,
c => c.HasMany(x => x.Elements).Inverse().Cascade.All());
}
}
public class BMap : ClassMap<B>
{
public B() {
Id(x => x.Id);
Map(x => x.Time);
}
}
When I save my entity, I have class A mapped to one table and class B to another as expected.
But I have nulls in Component_id column.
Can you tell me what am I missing here?
I believe Components are supposed to be in the same table , as clearly stated in Ayende's blog post, as they serve only to make the data better represented as an object model. Be sure to read through his blog, it's probably one of the best nHibernate resources out there.
Ok, I've resolved my problem - I can use Id of my "parent" class. So the component mapping will become:
public class AMap : ClassMap<A>
{
public A() {
Id(x => x.Id);
Component(x => x.Component,
c => c.HasMany(x => x.Elements).Cascade.All().Column("Id"));
}
}
So obvious as I look at it now ... but It took me an hour.
If you have a one-to-many association direct to a collection of components (ie. without the ComponentClass wrapper as per the question) then you can map it directly:
HasMany(x => x.Elements)
.AsSet()
.Table("ElementTable")
.KeyColumn("KeyColumn")
.Cascade.All()
.Component(x =>
{
x.Map(c => c.Id);
x.Map(c => c.Time);
})
.LazyLoad();