NHibernate merges two tables into one entity with a composite key

I have the following data structure:

+---------+ |Resume | +---------+ |Id (PK) | |IsActive | |... | |.. | |. | +---------+ +--------------------+ |Resume_Translation | +--------------------+ |ResumeId (PK, FK) | |Language (PK) | |Title | |Description | |... | |.. | |. | +--------------------+ 

So, I can have such data with two related tables:

 +----------------------------------------------------------+ |Id | IsActive | ResumeId | Language | Title | Description | +----------------------------------------------------------+ |1 | true | 1 | 'fr' | 'One' | 'One desc' | |1 | true | 1 | 'pl' | 'Raz' | 'Raz Opis' | |2 | true | 2 | 'fr' | 'B' | 'bla bla' | |3 | true | 3 | 'fr' | 'C' | 'C bla bla' | +----------------------------------------------------------+ 

From the point of view of my domain, I only care about the Resume entity. I do not want to have a Resume object with its Resume_Translations collection, because I will only have one Resume object with the current translation.

 public class Resume { public virtual int Id{ get; protected internal set; } public virtual string Language { get; protected internal set; } public virtual string Title { get; protected internal set; } public virtual string Description { get; protected internal set; } public virtual bool IsActive { get; protected internal set; } } 

My current mapping to Fluent NHibernate is as follows:

 public class ResumeMap : ClassMap<Resume> { public ResumeMap() { Table("Resume"); Id(x => x.Id); Map(x => x.IsActive); // other properties Join("Resume_Translation", m => { m.Fetch.Join(); m.Map(x => x.Language).Length(5); m.Map(x => x.Title).Length(100); m.Map(x => x.Description).Length(200); }); } } 

I can get what I want from the repository, without any problems, just passing the predicate Id Resume and Language to WHERE I want to.

However, I have some problems setting and updating the values.

My question is: how would I define a mapping that NHibernate Inserts a new record only into the Resume_Translation table instead of updating the record for the current object?

So what I want to achieve is if I have the following entry in my database:

 |2 | true | 2 | 'fr' | 'B' | 'bla bla' | 

Joining is good for relations between tables, so if I get it in my essence and I change the language and translation, nhibernate does the update, and I can understand it. If I try to add a new object with the same identifier in a different language and translation, nhibernate gives an error that the key already exists, and I also understand it.

So, of course, I am going the wrong way, but if someone can point me to the right decision on how I could achieve the comparison that I want, I would really appreciate it.

Another question, how do you feel about the entity and their translations from a business point of view?

Thank you for your help.

Thomas

+4
source share
3 answers

Stefan is on the right track. I changed his suggestion to have a bi-directional connection, which will simplify the update. One approach to this approach is that you need to manually assign the Resume property of the ResumeTranslation instance on insertion so that NHibernate correctly assigns the Resume table key to the ResumeTranslation row. So, given the associations you are building, here is how it would look in Fluent NH:

 public class ResumeTranslation { public virtual string Title { get; protected internal set; } public virtual string Description { get; protected internal set; } //Needed for bi-directional association: public virtual Resume Resume { get; set; } } public class ResumeTranslationMap : ClassMap<ResumeTranslation> { public ResumeTranslationMap() { Table("ResumeTranslation"); CompositeId() .KeyReference(kp => kp.Resume, "ResumeId") .KeyProperty(kp => kp.Language, "Language"); Map(x => x.Title); Map(x => x.Description); } } public class ResumeMap : ClassMap<Resume> { public ResumeMap() { Table("Resume"); Id(x => x.Id); Map(x => x.IsActive); // other properties HasMany(c => c.Translations) .Inverse() .KeyColumn("id") //May not be required but here for reference .Cascade.All(); } } 
+2
source

It seems that the attitude towards many refers to many. I personally would have a collection of ResumeTranslation objects in the Resume object. I would then compare this as standard for many.

You can then add another ActiveResumeTranslation property to your Entity Summary , which is a representative of your current translation .

+2
source

How about using a dictionary using language as a key?

 public class ResumeTranslation { public virtual string Title { get; protected internal set; } public virtual string Description { get; protected internal set; } } public class Resume { public virtual int Id{ get; protected internal set; } // language is the key to the translation // you may even want to hide the dictionary from the public interface of // this class and only provide access to a "current" language. public virtual IDictionary<string, ResumeTranslation> Translations { get; private set; } public virtual bool IsActive { get; protected internal set; } } 

And map it accordingly as a map to a composite element (sorry, I do not have fluency, so do not ask me how it will look). This exactly matches your database model.

+2
source

All Articles