2

I'm trying to setup a one-to-one mapping from my Users to the UserDetails table. Say I have the following tables in my database:

Users:

- UserID (PK, Identity)
- UserName
- Password

UsersDetails:

- UserID (PK, FK)
- FirstName
- LastName

I have created the following poco classes:

public class User {
    public virtual int UserID { get; set; }
    public virtual string UserName { get; set; }
    public virtual string Password { get; set; }
    public virtual UserDetails Details { get; set; }
}

public class UserDetails {
    public virtual int UserID { get; set; }
    public virtual User User { get; set; }
    public virtual string FirstName { get; set; }
    public virtual string LastName { get; set; }

    public UserDetails() {
    }

    public UserDetails(User user) {
        User = user;
    }
}

Which are fluently mapped (please note the xml mapping is very similar and if all you know is the xml mapping then I would still appreciate you guidance):

public class UserMap : ClassMap<User> {
    public UserMap() {
        Table("Users");
        Id(x => x.UserID);
        Map(x => x.UserName);
        Map(x => x.Password);
        HasOne(x => x.Details)
            .Constrained()
            .Cascade.All();
    }
}

public class UserDetailsMap : ClassMap<UserDetails> {
    public UserDetailsMap() {
        Table("UsersDetails");
        Id(x => x.UserID)
            .GeneratedBy.Foreign("User");
        HasOne(x => x.User)
            .Constrained();
        Map(x => x.FirstName);
        Map(x => x.LastName);
    }
}

Everything displays correctly but if I say:

var user = new User() { UserName = "Test", Password = "Test" };
user.Details = new UserDetails(user) { FirstName = "Test", LastName = "Test" };
session.Save(user);

I get the error:

"NHibernate.Id.IdentifierGenerationException: null id generated for: UserDetails."

I'd really appreciate it if someone could show me what I've done wrong. Thanks

Edit: Courtesy of Jamie Ide's suggestion. I have changed my User mapping to:

public class UserMap : ClassMap<User> {
    public UserMap() {
        Table("Users");
        Id(x => x.UserID);
        Map(x => x.UserName);
        Map(x => x.Password);
        References(x => x.Details, "UserID")
            .Class<UserDetails>()
            .Unique();
    }
}

But now when i insert a user i get the error:

"System.ArgumentOutOfRangeException: Index was out of range. Must be non-negative and less than the size of the collection."

If i add Cascade.All() on to my Reference i receive the original error i was getting about the null id generated.

nfplee
  • 7,643
  • 12
  • 63
  • 124

5 Answers5

2

I think Constrained should only be specified in the UserDetailsMap, not in the UserMap. Try:

public class UserMap : ClassMap<User> {
    public UserMap() {
        Table("Users");
        Id(x => x.UserID);
        Map(x => x.UserName);
        Map(x => x.Password);
        HasOne(x => x.Details)
            .Cascade.All();
    }
}

EDIT:

See this and this. It appears that you have to map the relationship as many-to-one from the User side and one-to-one constrained from the UserDetails side to get lazy loading working with one-to-one. I don't know if this is different in NH3.

Community
  • 1
  • 1
Jamie Ide
  • 48,427
  • 16
  • 81
  • 117
  • Cheers but lazy loading is not supported if you don't specify constrained on a one-to-one relationship. – nfplee Jan 12 '11 at 08:48
  • After further investigation. It appears this solves the problem but lazy loading is still not working. As soon as i grab the users it grabs the Details aswell. – nfplee Jan 12 '11 at 09:34
  • Thanks again, i've tried mapping the User side using a many to one mapping (i've updated my original question with the new mapping). But it's still not working. I'd very much appreciate your help once more. Thanks – nfplee Jan 13 '11 at 09:38
  • Try changing the mapping to: References(x => x.Details); – Jamie Ide Jan 13 '11 at 13:51
  • That won't work since there's no DetailsID field in the Users table. – nfplee Jan 13 '11 at 20:25
0

I am not sure the way you are mapping the ID property is correct. shouldnt it just be an autoincremented id and your mapping should be as follows:

Id(x=>x.UserId);

I dont know the idea behind using Foreign and how it fits into things here.. can you please illustrate the logic behind that?

Baz1nga
  • 15,485
  • 3
  • 35
  • 61
  • Cheers but the UserID field in the UsersDetails table is a reference to the Users table which contains the auto incrementing id. Because it is a one-to-one mapping, the user id in the UsersDetails table is also the primary key. Hope that helps. – nfplee Jan 11 '11 at 19:26
0

I think you need to have the foreign key identity mapping in your User mapping instead of the UserDetails and the native one in the UserDetails. I have been unable to find a reference for it, though.

Goblin
  • 7,970
  • 3
  • 36
  • 40
  • Thanks for your reply. I've just found the following article (http://nhforge.org/blogs/nhibernate/archive/2011/01/08/composite-with-only-a-many-to-one-bad-idea.aspx) on the NHibernate Blog. It's effectively what i wish to achieve but i still can't see how my mapping is any different. – nfplee Jan 11 '11 at 22:53
0

http://gorbach.wordpress.com/2010/09/24/fluent-onetoone/

HasOne(x => x.Details).Cascade.All().ForeignKey("UserDetails");
HasOne(x => x.User).Constrained();
var user = new User() { UserName = "Test", Password = "Test" };
user.Details = new UserDetails(user) { FirstName = "Test", LastName = "Test" };
user.Details.User = user;
session.Save(user);
kimyh00
  • 11
  • 1
0

After further research it appears that this won't work in version 2.0. I am now in a position to upgrade to version 3.0 where i believe this is now possible.

nfplee
  • 7,643
  • 12
  • 63
  • 124