Maxim Gershkovich
Maxim Gershkovich

Reputation: 47199

Mapping complex joins in nHibernate

I have used nHibernate in my latest project and successfully mapped all the basic relationships where the values existed in either the primary table I was working with or through a simple relationship like a composite.

Where I am getting stuck is how does one go about mapping complex joins?

For example I have an entity called Contact each contact has your usual properties like Name, DOB, Phone.... But I also need it to have a property called AccreditationList which would be a List<Accreditation>.

Here is a sample of the Contact XML declaration.

<class name="Contact" table="Contact" lazy="true">

    <id name="ID" column="ContactID" type="guid">
      <generator class="guid" />
    </id>

    <property name="FirstName">
      <column name="FirstName" sql-type="nvarchar(500)" not-null="true" />
    </property>

    <property name="LastName">
      <column name="LastName" sql-type="nvarchar(500)" not-null="true" />
    </property>

    <bag name="AccreditationList" lazy="true">    
        //How do I express the relationship here? 
    </bag>

</class>

The List<Accreditation> can only be determined through a series of joins like this.

SELECT Accreditation.* FROM CourseEnrolment 
INNER JOIN Course ON Course.CourseID = CourseEnrolment.CourseID
INNER JOIN CourseType ON CourseType.CourseTypeID = Course.CourseTypeID
INNER JOIN Accreditation ON Accreditation.AccreditationID = CourseType.AccreditationID
WHERE CourseEnrolment.ContactID = :ContactID

Is the only way to accomplish this by manually invoking the SQL through nHibernate in code with CreateSQLQuery or can I express this relationship using something like named queries? What is the correct approach? Any guidance would be appreciated.

Upvotes: 1

Views: 419

Answers (1)

J. Ed
J. Ed

Reputation: 6752

I see a couple of options:

  1. You can use SqlSelect, SqlUpdate etc.. to specify an arbitrary SQL statement for selecting / updating etc.. (a mapping-by-code example; I'm sure there's an XML equivalent)

  2. You could map your SQL query to a QueryOver query, and use it to initialize your Accreditation collection. Something along the lines of:

    public Contact GetContact(int id) { var contact = _session.Get(id); contact.AccreditationList = _session.QueryOver<Accreditation>() /* your query here */; return contact; }
    However!! this method has several drawbacks-

    • If you don't use the GetContact method, your collection won't be populated correctly.
    • It doesn't support Querying over Contact very easily (first you have to query for the contacts, and then you have to initialize each contact's accreditation list).
  3. You could map the intermediate entity (Course) to your Contact entity (you can have it as a private member, if you don't want to expose them), and then your AccreditationList property would be

public IEnumerable<Accreditaion> AccreditationList { get { return _courses.SelectMany(course => course.Accreditations); } } You wouldn't be able to manipulate the Contact's AccreditationList directly, though.

The best solution would be to have something like QueryOverSelect option in the mapping, but AFAIK there isn't...
Personally I'd go with the 3rd option, since it seems the cleanest to me.

Upvotes: 1

Related Questions