Fluent NHibernate only generating one extra column for multiple HasMany relationships - c#

I have a thesaurus application built on NHibernate. The data model is very straight-forward:
public class Word
{
public virtual int Id { get; set; }
public virtual string Text { get; set; }
public virtual IList<Word> Synonyms { get; set; }
public virtual IList<Word> Antonyms { get; set; }
}
So each word has a list of synonyms and a list of antonyms. I thought the mapping would be straight-forward as well:
public class WordMapping : ClassMap<Word>
{
public WordMapping()
{
Id(x => x.Id);
Map(x => x.Text);
HasMany(x => x.Synonyms).Cascade.AllDeleteOrphan();
HasMany(x => x.Antonyms).Cascade.AllDeleteOrphan();
}
}
When I look at the table generated in MS SQL Server 2008, I only see one key column for the two HasMany relations:
Id int
Text nvarchar(255)
Word_id int
I think this is causing some weird things to happen when I insert data. When I get the lists after doing some insertions, both Synonyms and Antonyms contain the same Words, but the Words are just seemingly-arbitrary subsets of what should be in the two lists.
Do I have the problem pegged right? If so, how can I fix it?

I was way off base. I assumed FNH was generating a separate table for the HasMany relationships (it wasn't). Proper code found here: Fluent NHibernate Self Referencing Many To Many
Specific to my problem, I created two separate tables:
-- Synonyms table --
WordId int
SynonymId int
-- Antonyms table --
WordId int
AntonymId int
Then in my mapping class:
public class WordMapping : ClassMap<Word>
{
public WordMapping()
{
Id(x => x.Id);
Map(x => x.Text);
HasManyToMany(x => x.Synonyms).ParentKeyColumn("WordId")
.ChildKeyColumn("SynonymId")
.Table("Synonyms");
HasManyToMany(x => x.Antonyms).ParentKeyColumn("WordId")
.ChildKeyColumn("AntonymId")
.Table("Antonyms");
}
}
That fixes all the issues.

Related

NHibernate Parent and Child Tables have different number of keys in their composite

I'm struggling a bit with an NHibernate mapping within a console app.
Here is the summary:
It's a one to many relationship. PARENT_TABLE (one) to CHILD_TABLE (many)
PARENT_TABLE has a composite key consisting of two keys: Vendor and
Invoice
Id(x => x.Invoice).Column("INVOICE");
References(x => x.Distribution).Column("INVOICE");
CHILD_TABLE has a composite key consisting of three: Vendor,
Invoice, and Num
Id(x => x.Invoice).Column("INVOICE");
HasOne(x => x.Invoice).ForeignKey("INVOICE").Cascade.Refresh();
When I run through some data that has the following example:
Vendor Invoice Num
10 | 44 | 1
11 | 44 | 1
11 | 44 | 2
I end up getting an error because of the duplicate ID in Invoice (Makes total sense)
Then, I tried using a composite ID using 3 keys for Distribution and 2 keys for Invoice. This, as I'm sure many of you already know, also ended up with an error since there are two records that use 11 and 44 for Vendor and Invoice, respectively.
My question: Is there a way to declare the relationship using a composite ID (INVOICE and VENDOR) and still have the child collection respect/enforce the uniqueness of the 3 key composite (INVOICE, VENDOR, and NUM)?
I've tried several permutations of composite keys and haven't been able to figure it out. Maybe it's just not designed for this. Any help would be much appreciated!
Given you have a composite id definition on your entities, you have to map it using the CompositeId method. In your child entity, you also have to define the references for key columns of your parent entity, because it is a composite id (and that's the reason where I personally do not enjoy working with composite IDs).
Considering your model:
public class ParentEntity
{
public virtual int Vendor { get; set; }
public virtual int Invoice { get; set; }
// other properties
}
public class ChildEntity
{
public virtual int Vendor { get; set; }
public virtual int Invoice { get; set; }
public virtual int Num { get; set; }
public virtual ParentEntity ParentEntity { get; set; }
}
Your mappings could be like this:
public class ParentEntityMap : ClassMap<ParentEntity>
{
public ParentEntityMap()
{
CompositeId()
.KeyProperty(x => x.Vendor, "VENDOR")
.KeyProperty(x => x.Invoice, "INVOICE");
// other mappings ...
}
}
public class ChildEntityMap : ClassMap<ChildEntity>
{
public ChildEntity()
{
CompositeId()
.KeyProperty(x => x.Vendor, "VENDOR")
.KeyProperty(x => x.Invoice, "INVOICE")
.KeyProperty(x => x.Num, "NUM");
References(x => x.ParentEntity)
.Columns("VENDOR", "INVOICE")
.ReadOnly(); // if you are mapping the same columns of compositeId here, define it as readOnly to avoid inserting data two times on the same columns.
}
}
I did not try this code, it is just a suggestion to check what could work for you.

Fluent NHibernate: One to One Mapping - reverse from primary column to a foreign key

I have a "Group" class and a "GroupSummaryLevel" class, codes are given below. There is a one-to-one relation between these entities in DB. I need the "GroupSummaryLevel" as a property in Groups class. It is supposed to be a very simple join like
(SELECT g.Id FROM GroupSummaryLevel g WHERE g.AcctGroup = GroupID)
Unfortunately, I wasn't able to figure this out how to do with NHibernate. The many answers I saw here is no help to me. I would appreaciate any inputs from the more experienced NHibernate users out there. Thanks in advance.
public class Group : DomainEntity
{
public virtual string GroupId { get; set; }
public virtual string GroupName { get; set; }
public virtual GroupSummaryLevel GroupSummaryLevel { get; set; }
}
public class GroupSummaryLevel : DomainEntity
{
public virtual int Id { get; set; }
public virtual string AcctGroup { get; set; }
public virtual GroupSummaryLevel Parent { get; set; }
public virtual IList<GroupSummaryLevel> Children { get; set; }
public GroupSummaryLevel()
{
Children = new List<GroupSummaryLevel>();
}
}
The mapping I have done did not work so far. My mapping codes are as below:
public GroupMap()
{
Table("Groups");
LazyLoad();
Id(x => x.GroupId).GeneratedBy.Assigned().Column("GroupID").CustomType<TrimmedString>();
Map(x => x.GroupName).Column("GroupName").CustomType<TrimmedString>().Not.Nullable();
HasOne(x => x.GroupSummaryLevel).Cascade.None().ForeignKey("AcctGroup");
}
public GroupSummaryLevelMap()
{
Table("GroupSummaryLevel");
LazyLoad();
Id(x => x.Id).GeneratedBy.Identity().Column("Id");
Map(x => x.AcctGroup).Column("AcctGroup").CustomType<TrimmedString>().Not.Nullable();
//References(x => x.Parent).Column("ParentId");
//HasMany(x => x.Children).Cascade.All().KeyColumn("ParentId");
}
Note: I also need to do a self-join for GroupSummaryLevel, and no success with that either. Any recommendations for that will also be appreciated :)
I would say, that your one-to-one is not driven by primary/foreign keys, but by property-ref. So the Group should map the summary by saying something like this:
...if you want to find related SummaryLevel, pass my <id> into column mapped as AcctGroup
public GroupMap()
{
...
HasOne(x => x.GroupSummaryLevel)
.Cascade.None()
//.ForeignKey("AcctGroup")
.PropertyRef(gsl => gsl.AcctGroup)
;
}
public GroupSummaryLevelMap()
{
...
//References(x => x.Parent).Column("ParentId");
//HasMany(x => x.Children).Cascade.All().KeyColumn("ParentId");
References(x => x.Parent, "AcctGroup");
}
NOTEs for completeness, as discussed in comments:
In this scenario, when the "child" has reference to parent - it really calls for one-to-many/.HasMany() mapping.
The down side is, that child is represented as a colleciton of children: IList<GroupSummaryLevel>. It is not as straighforward to use, but we can create some virtual property, returning the .FirstOrDefault(). The benefit we get - is lazy loading (not in place with one-to-one).

Nhibernate stores id=0 as null

I have small problem with nHibernate (fluent) I have two objects, one contains another - a parent and a child (predefined objects, readonly).
mappings:
public class ParentClass
{
public virtual int Id { get; set; }
public virtual ChildClass Metoda { get; set; }
}
public ParentClassMap() {
Table("Wyceny");
Id(x => x.Id).Column("Id").GeneratedBy.TriggerIdentity();
References(x => x.Metoda).Column("RMW_ID");
}
public ChildClass
{
public virtual int Id { get; set; }
public virtual string Nazwa { get; set; }
}
public ChildClassMap()
{
Table("Metody");
Id(x => x.Id).Column("Id");
Map(x => x.Nazwa).Column("Nazwa_met");
}
Everything works perfectly until I chose child object with id = 0, reading still works for id=0, but when I'm trying to save or update Parent with correct ChildObject(readed previously from db through nHibernate), nHibernate stores null instead of value.
Any suggestions?
nHibernate 3.3.1.4000
fluent 1.4.0.0
The issue here would be the unsaved-value. NHibernate must decide if operations with object will be insert or update. This decision comes from unsaved-value setting, which is by default for int set to 0.
Try to extend your mapping of a ChildClass:
public ChildClassMap()
{
Table("Metody");
Id(x => x.Id)
.Column("Id")
.UnsavedValue(-1);
...
See 5.1.4. id, cite:
unsaved-value (optional - defaults to a "sensible" value): An identifier property value that indicates that an instance is newly instantiated (unsaved), distinguishing it from transient instances that were saved or loaded in a previous session.
And here is nice Id mapping overview by Adam Bar (the second half of the article)

Nhibernate self reference performance

I have a model that looks like this:
public class ComponentAttributeDto
{
public virtual long ComponentAttributeId { get; set; }
public virtual ComponentAttributeDto ParentComponentAttributeDto { get; set; }
public virtual string ComponentAttributeName { get; set; }
public virtual string Value { get; set; }
public virtual DataType DataType { get; set; }
public virtual IList<ComponentAttributeDto> ChildComponentAttributes { get; set; }
}
With a mapping file of:
public class ComponentAttributeMapping : ClassMap<ComponentAttributeDto>
{
public ComponentAttributeMapping()
{
Table("ComponentAttributes");
Id(x => x.ComponentAttributeId)
.GeneratedBy.Identity();
References(x => x.ParentComponentAttributeDto)
.Column("ParentComponentAttributeId");
HasMany(x => x.ChildComponentAttributes)
.Fetch.Select()
.Inverse()
.Cascade.AllDeleteOrphan()
.KeyColumn("ParentComponentAttributeId");
Map(x => x.ComponentAttributeName)
.Length(50);
Map(x => x.Value)
.Length(1500);
Map(x => x.DataType)
.Length(20);
}
}
When loading this with a large dataset that goes about 4 levels deep the performance is terrible. When running profiler I noticed that is it executing a select statement for every single value in the table for the data I want to lookup. Is there a way I can improve the performance to do some type of join on the table or something else?
You could use batch-size to pre-fetch instances, which reduces the number of queries considerably.
mapping (not sure if it is supported by Fluent in the meanwhile):
HasMany(x => x.ChildComponentAttributes)
.Fetch.Select()
.SetAttribute("batch-size", "20")
.Inverse()
.Cascade.AllDeleteOrphan()
.KeyColumn("ParentComponentAttributeId");
If you had a Root property, you could make a query for the whole tree at once.
public class ComponentAttributeDto
{
public virtual ComponentAttributeDto ParentComponentAttributeDto { get; private set; }
public virtual ComponentAttributeDto Root
{
get
{
if (ParentComponentAttributeDto == null)
{
return this;
}
else
{
return ParentComponentAttributeDto.Root;
}
}
private set
{ /* just for NH to call it */ }
}
// ....
}
HasMany(x => x.Children).AsSet().SetAttribute("batch-size", "20")
the query
session.CreateQuery(
#"from ComponentAttributeDto
where Root = :root"
.SetEntity(root);
should actually result in only a single query. Not sure if NH doesn't actually perform queries for the lists (ChildComponentAttributes), but it is worth a try.
Do you need the entire data structure all at once? Usually when I run into this problem I just take away the mapping handling from nHibernate and deal with it myself. Create a method for the class called getChildren() and have it run the query when called. If you want to add a child record then add another method called addChild() and have it instantiate with it's own parent ID.
You could eagerly fetch the hierarchy when you query. You can do this by using the eager fetch option in your query:
Session.QueryOver<ComponentAttributeDto>
.Fetch(a => a.ChildComponentAttributes).Eager
Down to the level you want to fetch.

Fluent / NHibernate Collections of the same class

I am new to NHibernate and I am having trouble mapping the following relationships within this class.
public class Category : IAuditable
{
public virtual int Id { get; set; }
public virtual string Name{ get; set; }
public virtual Category ParentCategory { get; set; }
public virtual IList<Category> SubCategories { get; set; }
public Category()
{
this.Name = string.Empty;
this.SubCategories = new List<Category>();
}
}
Class Maps (although, these are practically guesses)
public class CategoryMap : ClassMap<Category>
{
public CategoryMap()
{
Id(x => x.Id);
Map(x => x.Name);
References(x => x.ParentCategory)
.Nullable()
.Not.LazyLoad();
HasMany(x => x.SubCategories)
.Cascade.All();
}
}
Each Category may have a parent category, some Categories have many subCategories, etc, etc
I can get the Category to Save correctly (correct subcategories and parent category fk exist in the database) but when loading, it returns itself as the parent category.
I am using Fluent for the class mapping, but if someone could point me in the right direction for just plain NHibernate that would work as well.
By convention, Fluent NHibernate will look at "Category_Id" as foreign key column. It won't figure out your "ParentCategoryId" column. Unless you rename your self-referencing column to "Category_Id", you have to clarify the column name for both parent and child relationships.
For category without parent (absolute-parent category), whose reference column is null, it's reasonable to return itself as parent or null depending on how NHibernate handles it since you choose eager loading.
public class CategoryMap : ClassMap<Category>
{
public CategoryMap()
{
Id(x => x.Id);
Map(x => x.Name);
References(x => x.ParentCategory)
.Column("ParentCategoryId") // Many-To-One : parent
.Nullable()
.Not.LazyLoad();
HasMany(x => x.SubCategories)
.Cascade.All().Inverse().KeyColumn("ParentCategoryId"); //One-To-Many : chidren
}
}
Ok so on the HasMany(x=>x.SubCategories) you need to add Inverse() to the call chain and also give it the column name which I'm assuming is "ParentCategoryId" given the mapping of the parent category, of course this depends on your conventions too.
If you were to post your table stucture I can give you a complete solution.

Categories