5

I have the following database structure, which I cannot change:

CREATE TABLE Users (
    ID INT NOT NULL IDENTITY(1,1),
    PRIMARY KEY(ID)
)

CREATE TABLE UserAvatars (
    UserID INT NOT NULL,
    Width INT NOT NULL,
    Height INT NOT NULL,

    PRIMARY KEY(UserID),
    FOREIGN KEY(UserID) REFERENCES Users(ID),
)

Basically, a User can have Zero-or-one avatars (I removed columns to simplify the example).

class User
{
    public int ID { get; protected set; }

    public UserAvatar Avatar { get; set; }
}

class UserAvatar
{
    public User User { get; set; }
    public int Width { get; set; }
    public int Height { get; set; }
}

I am using the following mapping:

class UserMapping : ClassMapping<User>
{
    public UserMapping() 
    {
        Id(x => x.ID, m => {
            m.Generator(Generators.Identity);
        });

        OneToOne(x => x.Avatar);
    }
}

class UserAvatarMapping : ClassMapping<UserAvatar>
{
    public UserAvatar() 
    {
        Id(x => x.User, m =>
        {
            m.Generator(Generators.Foreign<User>(u => u.ID));
        });

        Property(x => x.Width);
        Property(x => x.Height);
    }
}

However, when trying run my app, I get an NHibernate.MappingException:

NHibernate.MappingException : Could not compile the mapping document: mapping_by_code----> NHibernate.MappingException : Could not determine type for: MyApp.Models.UserAvatar, MyApp, for columns: NHibernate.Mapping.Column(User)

I can't make any sense of this cryptic error.

How do I accomplish the mapping where a User could have zero or one Avatars?

Radim Köhler
  • 122,561
  • 47
  • 239
  • 335
Matthew
  • 24,703
  • 9
  • 76
  • 110

2 Answers2

4

Taking from Radim Köhler's guidence, the final classes / mapping that solved the problem:

class User
{
    public int ID { get; protected set; }
    public UserAvatar Avatar { get; set; }
}

class UserAvatar
{
    public int UserID { get; protected set; }
    public User User { get; set; }
    public int Width { get; set; }
    public int Height { get; set; }
}

With the following mapping:

class UserMapping : ClassMapping<User>
{
    public UserMapping() 
    {
        Id(x => x.ID, m => {
            m.Generator(Generators.Identity);
        });

        OneToOne(x => x.Avatar, m =>
        {
            m.Cascade(Cascade.All);
            m.Constrained(false);
        });         
    }
}

class UserAvatarMapping : ClassMapping<UserAvatar>
{
    public UserAvatar() 
    {
        Id(x => x.UserID, m =>
        {
            m.Generator(Generators.Foreign<UserAvatar>(u => u.User));
        });

        OneToOne(x => x.User, m =>
        {
            m.Constrained(true);
        });         

        Property(x => x.Width);
        Property(x => x.Height);
    }
}
Matthew
  • 24,703
  • 9
  • 76
  • 110
2

The ID is important almost for every entity mapped by NHibernate (ORM). So, we should extend the Avatar class:

class UserAvatar
{
    // every(stuff) should have its ID
    public int ID { get; protected set; } 
    public User User { get; set; }
    ...

The ID value will be managed by one-to-one relationship. So, now let's adjust the mapping.

public UserAvatar() 
{
    Id(x => x.ID);

    HasOne(x => x.User)
      .Constrained()
      .ForeignKey();
    ...

public UserMapping() 
{
    Id(x => x.ID);
    HasOne(s => s.Avatar).Cascade.All();

Related and really interesting reading:

Community
  • 1
  • 1
Radim Köhler
  • 122,561
  • 47
  • 239
  • 335
  • Thanks for this, I'll try this tomorrow when I get to work. Do you have any reference for mapping-by-code (not fluent nhibernate)? No worries if you don't. – Matthew Dec 04 '13 at 04:11
  • Yes, there are stories how to use the mapping by code. One-to-one is here: http://notherdev.blogspot.cz/2012/01/mapping-by-code-onetoone.html, the *index* of them is here: http://notherdev.blogspot.cz/2012/02/nhibernates-mapping-by-code-summary.html – Radim Köhler Dec 04 '13 at 04:27
  • Thanks @Radim, this helped greatly in solving the problem. I'm going to post my solution which uses mapping-by-code. – Matthew Dec 04 '13 at 15:03
  • Great to see that. Enjoy NHibernate, amazing tool – Radim Köhler Dec 04 '13 at 15:05