Fluent nHibernate mapping opposite side of References - nhibernate

I've got a table structure similar to the below:
AttributeKey (ID, Title)
AttributeValue (ID, AttributeKeyID, Value)
I've got my mapping all setup so that if I query AttributeValue, it joins to AttributeKey (AttributeValue.AttributeKeyID = AttributeKey.ID) using References, no problem... however, I want to query the AttributeKey table and LEFT JOIN onto the AttributeValue table so that I get all AttributeKeys and any values ascociated to them... how do I map this in fluent nHibernate?
Essentially it's the reverse of a References mapping.
EDIT: I'm aware that the HasMany method is meant to be the reverse of References but using that throws the exception that my property doesn't implement UserCollectionType
Cheers

This should be a HasMany in your AttributeKey ClassMap. Your class should look something like this:
public class AttributeKey
{
public int Id { get; set; }
public string Title { get; set; }
public IList<AttributeValue> AttributeValues { get; set; }
}
Your mapping would then look like this:
public class AttributeKeyMap : ClassMap<AttributeKey>
{
public AttributeKeyMap()
{
Id(x => x.Id, "ID");
Map(x => x.Title);
HasMany(x => x.AttributeValues)
.KeyColumn("AttributeKeyID")
.Inverse()
.Cascade.AllDeleteOrphan();
}
}

Related

Fluent NHibernate Many to One Mapping Problem

Edit
I have the answer and will post it up tomorrow.
I have a class that appears to be using the parent primary key column instead of it's own primary key column.
There are two classes involved here ActionHistory and ActionData. The parent class, ActionHistory, represents an action and details who performed the action. The child class, ActionData, represents a piece of data that was created by the action. Each ActionHistroy has a number of ActionData and this is represented through a many to one mapping.
The classes are:
public class ActionHistory
{
public virtual int Code { get; set; }
public virtual string Doneby { get; set; }
public virtual IList<ActionData> _ActionData{ get; set;}
}
public class ActionData
{
public virtual int Code { get; set; }
public virtual double Data{ get; set; }
public virtual ActionHistory _ActionHistory{ get; set; }
}
My mappings are:
public class ActionHistoryClassMap : ClassMap<ActionHistory>
{
public ActionHistoryClassMap ()
{
Table("ACTIONHISTORY");
Id(x => x.Code, "CODE").GeneratedBy.Assigned();
Map(x => x.Doneby, "DONEBY");
HasMany(x => x._Actiondata).KeyColumn("CODE").AsBag().Cascade.All();
}
}
public class ActionDataClassMap : ClassMap<ActionData>
{
public ActionDataClassMap ()
{
Table("ACTIONDATA");
Id(x => x.Code, "CODE").GeneratedBy.Assigned();
Map(x => x.Data, "DATA");
References(x => x._ActionHistory, "ACTIONHISTORYID");
}
}
The problem is when an ActionHistory class is queried the correct ActionHistory is returned but the HistoryData that it contains is wrong. A list of HistoryData should be returned but only one HistoryData is returned and it's Code value is the Code values of the parent ActionHistory class. I believe my problem stems form the fact that both classes have the same primary key column name and I'm not handling this properly. The columns that that are related are CODE from the ACTIONHISTORY table and ACTIONHISTORYID from the ACTIONDATA table. The database is fixed so I can't rename any columns. In this case data is only read from the database and not saved or updated.
I downloaded NHibernate Profiler to help me with this problem and have found the exact point in the SQL where the problem occurs.
This is the SQL query that is generated to return the ActionData (where 18 is the ActionHistory primary key):
SELECT actiondat0_.CODE as CODE1_,
actiondat0_.CODE as CODE9_0_,
actiondat0_.DATA as DATA9_0_,
actiondat0_.ACTIONHISTORYID as ACTIONHI4_9_0_
FROM ACTIONDATA actiondat0_
WHERE actiondat0_.CODE = 18 /* #p0 */
The last line should be:
WHERE actiondat0_.ACTIONHISTORYID = 18 /* #p0 */
But I just do not understand why the mapping is not generating the right query.
I know it was something blindingly obvious and after hours of getting nowhere it of course jumps out at me.
The ActionHistory class map should of course be:
public class ActionHistoryClassMap : ClassMap<ActionHistory>
{
public ActionHistoryClassMap ()
{
Table("ACTIONHISTORY");
Id(x => x.Code, "CODE").GeneratedBy.Assigned();
Map(x => x.Doneby, "DONEBY");
HasMany(x => x._Actiondata).KeyColumn("ACTIONHISTORYID")
.AsBag().Cascade.All();
}
}
The difference being the KeyColumn method is now ACTIONHISTORYID as opposed to CODE. I didn't cop that KeyColumn should reference the ActionHistory key column. It seems obvious now and it didn't help that all other times I implemented it in this project the columns with the relationship always had the same name.

How to only get the Ids from a FluentNhibernate HasManyToMany mapping

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.

Fluent NHibernate Relationship Mapping and Save Exception

I'm new to NHibernate and am attempting to use Fluent's AutoMapping capability so that I do not need to maintain separate XML files by hand. Unfortunately I'm running into a problem with referenced entities, specifically 'Exception occurred getter of Fluent_NHibernate_Demo.Domain.Name.Id' - System.Reflection.TargetException: Object does not match target type.
I appear to have an error in at least one of my mapping classes although they do generate the correct SQL (i.e. the created tables have the correct indexes).
The implementations for my domain models and mappings are:
Name.cs
public class Name
{
public virtual int Id { get; protected set; }
public virtual string First { get; set; }
public virtual string Middle { get; set; }
public virtual string Last { get; set; }
}
Person.cs
public class Person
{
public virtual int Id { get; protected set; }
public virtual Name Name { get; set; }
public virtual short Age { get; set; }
}
NameMap.cs
public NameMap()
{
Table("`Name`");
Id(x => x.Id).Column("`Id`").GeneratedBy.Identity();
Map(x => x.First).Column("`First`").Not.Nullable().Length(20);
Map(x => x.Middle).Column("`Middle`").Nullable().Length(20);
Map(x => x.Last).Column("`Last`").Not.Nullable().Length(20);
}
PersonMap.cs
public PersonMap()
{
Table("`Person`");
Id(x => x.Id).Column("`Id`").GeneratedBy.Identity();
References<Name>(x => x.Name.Id, "`NameId`").Not.Nullable();
// There's no exception if the following line is used instead of References
// although no constraint is created
// Map(x => x.Name.Id).Column("`NameId`").Not.Nullable();
Map(x => x.Age).Column("`Age`").Nullable();
}
Finally, the following code will produce the exception:
Name name = new Name { First = "John", Last = "Doe" };
session.Save(name);
Person person = new Person { Name = name, Age = 22 };
session.Save(person); // this line throws the exception
As mentioned, the created schema is correct but I'm unable to save using the above code. What is the correct way to create a foreign key constraint using Fluent NHibernate?
If you want to reference the name, by ID, then that's what you should do. NHibernate is smart enough to figure out what the actual FK field on Person should be and where it should point; that is, after all, the job an ORM is designed to perform.
Try this mapping:
public PersonMap()
{
Table("`Person`");
Id(x => x.Id).Column("`Id`").GeneratedBy.Identity();
References(x => x.Name, "`NameId`").Not.Nullable();
Map(x => x.Age).Column("`Age`").Nullable();
}
You've mapped the person and the name; as a result, NHibernate knows which property is the ID property of Name, and can create and traverse the foreign key on Person.

FluentNHibernate Unidirectional One-To-Many Mapping

I have two classes. One is Order:
public class Order
{
public virtual int Id { get; set; }
public virtual IList<Product> Products { get; set; }
}
The other one is Product:
public class Product
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
}
They are fluently mapped like this:
public class OrderMap : ClassMap<Order>
{
public OrderMap()
{
Table("Orders");
Id(x => x.Id, "Id");
HasMany(x => x.Products)
.KeyColumn("OrderId")
.Cascade.All();
}
}
public class ProductMap : ClassMap<Product>
{
public ProductMap()
{
Table("Products");
Id(x => x.Id, "Id");
Map(x => x.Name);
}
}
The database does NOT have a not-null constraint on the OrderId column of the Products table.
Problem is: both the order and the products are being persisted, however the products are being persisted with a null value on the OrderId column.
Am I missing something?
instead of HasMany just use Reference
Reference(x => x.Products).Cascade.All();
Inverse on HasMany is an NHibernate term, and it means that the other end of the relationship is responsible for saving.
Well, as strange as it seems, we solved this issue here by re-working our Session management, using it to create an ITransaction in a using statement. Odd, but solved. Thanks everyone!
Try using:
HasMany(x => x.Products)
.KeyColumn("OrderId")
.Inverse()
.Cascade.All();
Which(Inverse()) states that OrderId is on the Products table

Fluent NHibernate Inheritance Issue

I'm using Nhibernate with Fluent and I encounter an issue with inheritance.
Here my DB schema
TABLE Base
IDBASE (PK)
Field1
TYPE
TABLE T1
IDBASE (PK and FK)
Field2
Field3 ...
My mapping files are :
public class BaseMap: ClassMap<BASE>
{
public BaseMap()
{
Id(x => x.Id, "IDBASE").GeneratedBy.Identity();
Map(x => x.Field1);
DiscriminateSubClassesOnColumn("TYPE");
}
}
public class T1Map: SubclassMap<T1>
{
public T1Map()
{
Table("T1");
KeyColumn("IDBASE");
DiscriminatorValue("T1");
Map(x => x.Field2).Not.Nullable();
Map(x => x.Field3).Not.Nullable();
}
}
I use FluentMappings instead of AutoMapping.
Here my entities :
public abstract class BASE
{
public virtual long IdBase{ get; set; }
public virtual string Field1 { get; set; }
}
public class T1: BASE
{
public virtual string Field2 { get; set; }
public virtual string Field3 { get; set; }
}
T1 entity inherits from BASE entity, the issue is when I try to get a row NHibernate try to select Field2 and Field3 on the Base Table whereas they should be selected on T1 Table.
I've tried dozens of hacks but it still doesn't work, if anyone as an idea it would be very helpful.
Thanks a lot.
You're specifying a discriminator, that implies that the inheritance structure should be a table-per-class-hierarchy; you won't have two tables with this setup, you'll have a single table with everything in (hence why the select is hitting the same table for all the columns).
If you remove the DiscriminateSubclassesOnColumn call, that'll put your mappings into a table-per-class, so you'll have your desired structure.
Actually I really need the DiscriminateSubclassesOnColumn so I found an other solution, in my inherited entity I do the mapping with the join like this :
public class T1Map: SubclassMap<T1>
{
public T1Map()
{
Join("T1", y =>
{
y.KeyColumn("IDBASE");
y.Map(x => x.Field2).Not.Nullable();
y.Map(x => x.Field3).Not.Nullable();
});
}
}
And it works fine, I can keep the Discriminator column on my base class.
Thanks