views:

65

answers:

2

Hello,

I have a User which can have many Emails. This is mapped through a List collection (exposed by IEnumerable Emails on the User). For each User one of the Emails will be the Primary one ("Boolean IsPrimary" property on Email).

How can I get the primary Email from User without NHibernate loads every email for the User ?

I have the following two entities, with a corresponding table for each

public class User
{
   private readonly IList<Email> _emails;

   public virtual int Id { get; private set; }
   public virtual IEnumerable<Email> Emails { get { return _emails; } }
   // public virtual Email PrimaryEmail { get; set; } - Possible somehow ?
}
public class Email
{
   public virtual int Id { get; private set; }
   public virtual String Address { get; set; }
   public virtual Boolean IsPrimary { get; set; }
   public virtual User User { get; set; }
}

Can I map a "Email PrimaryEmail" property etc. on the User to the Email which have "IsPrimary=1" set somehow ? Maybe using a Sql Formula ? a View ? a One-To-One relationship ? or another way ?

It should be possible to change the primary email to be one of the other emails, so i would like to keep them all in 1 table and just change the IsPrimary property.

Using a Sql Formula, is it be possible to keep the "PrimaryEmail" property on the User up-to-date, if I set the IsPrimary property on the current primary email to false, and then afterwards set the PrimaryEmail property to the email which should be the new primary email and set IsPrimary to true ? Will NHibernate track changes on the "old/current" primary Email loaded by the Sql Formula ? What about the 1 level cache and the 2 level cache when using SqlFormula ?

I dont know if it could work by using a View ? Then i guess the Email could be mapped like a Component ? Will it work when updating the Email data when loaded from the View ?

Is there a better way ?

As I have a bi-directional relationship between User and Email I could in many cases of course query the primary Email and then use the "User" property on the Email to get the User (instead of the other way around - going from User to the primary Email)

Hope someone can help ?

+1  A: 

At first glance, I guess I would want to ensure that the PrimaryEMail is always a reference to one of the EMail objects in the collection (one way of doing that is to have the get/set be completely on-the-fly). With an all-public design like you have, you'll have to hook the change with an event handler on the EMail and/or the collection. The new primary email object (something in the collection which you are promoting to primary) will be aware of its parent collection or user, so should be able to find the current primary to make it secondary.

Alternatives include making the IsPrimary not be a public set, or even not exposing it publicly at all (email.IsPrimary would be replaced with email == user.PrimaryEMail). PrimaryEMail set method might only accept an EMail object already in the collection.

I've only done this in manual ORM classes, and could not find a canonical method for performing this fairly common scenario in NHibernate. There must be one out there.

Cade Roux
+1  A: 

First of all. Why are you using an IEnumerable there? Do you want to launch a query each time that property gets accessed? And why does it have a public setter?

Seems to me you want that to be either a IList or ICollection. Having a list would make more sense as I would add two methods:

User.SetPrimary(int index)
User.GetPrimary()
User.ICollection Emails { get; protected set; }

Email.IsPrimary { get; protected set; }

The first iterating through the e-mails to make sure only one item is set to primary and the second that iterates through the e-mails until one is found that is marked as primary.

Ramon
Hello, Thanks for your answer. I have a private IList<Email> field mapped in the background, but it is exposed by a public IEnumerable<Email>. Also the setter should of course be private/protected - it is not my actual code but a quick example i wrote for making it more simple (ill update it). In this case loading all emails to iterate them could work, but what about a scenario with album/pictures where i just need one picture instead of loading maybe 100 pictures to just find one ? I would like it to not load all from the database but only get the one needed.
MartinF
So lazy loading of the picture data. It depends on how you want to access the photo. If you already have the key then it would be logical to access the photo record directly. For example the following url's/user/photos/photo/photoId/user/photos/album/albumIdBy the way.. although a user is the owner of an email does not make it part of the user. So a emails collection on the user class is not correctly modelled. Just reflect it on real life. You are the owner of your house and/or car but your house or car is not part of you. If just hapens that you have a relation with your car or house.
Ramon