2

I'm kinda new to nhibernate and i ran into a problem. I have the following tables:

Table 1: Table Name: Users, Column 1: ID, Column 2: Name

Table 2: Table Name: Missions, Column 1: ID, Column 2: Description

Table 3: Table Name: UserToDoMissions, Column 1: UserID, Column 2: MissionID, Column 3: Rank

Here is the code: MissionMap:

public class MissionMap : ClassMap<Mission>
{
    public const string TableName = "tblMissions";
    public const string c_id = "achID";
    public const string c_name = "achName";

    public MissionMap()
    {
        Table(TableName);
        Id(x => x.ID).Column(c_id).Not.Nullable();
        Map(x => x.Name).Column(c_name).Not.Nullable();

        HasMany(x => x.UserToDoList).Cascade.All().Inverse().KeyColumn(UserToDoMap.c_missionID);
    }
}

User Map:

public class UserMap : ClassMap<User>
{
    public const string TableName = "tblUsers";
    public const string c_id = "usrID";
    public const string c_userName = "usrUserName";

    public UserMap()
    {
        Table(TableName);
        Id(x => x.ID).Column(c_id).Not.Nullable();
        Map(x => x.UserName).Column(c_userName).UniqueKey(DBConsts.UniqueKeys.User_UserName).Not.Nullable();

        HasMany(x => x.UserToDoList).Cascade.All().Inverse().KeyColumn(UserToDoMap.c_userID);
    }
}

UserToDo Map:

public class UserToDoMap : ClassMap<UserToDo>
{
    public const string TableName = "tblToDoList";
    public const string c_userID = "tdlUserID";
    public const string c_missionID = "tdlMissionID";
    public const string c_rank = "tdlRank";

    public UserToDoMap()
    {
        Table(TableName);
        CompositeId().KeyReference(x => x.User, c_userID).KeyReference(x => x.Achievment, c_missionID);
        References(x => x.User).Column(c_userID).Cascade.All().UniqueKey(DBConsts.UniqueKeys.ToDoList_UserRanks).Not.Nullable();
        References(x => x.Mission).Column(c_missionID).Cascade.All().Not.Nullable();
        Map(x => x.Rank).Column(c_rank).UniqueKey(DBConsts.UniqueKeys.ToDoList_UserRanks).Not.Nullable();
    }
}

My problem is that i can;t save a new line. I can select and get all of the things but can't save. Any ideas?

Thanks.

Edit: I'm not getting anything. No exception is thrown but the row isn't added to the DB. BTW, I'm using the save method on UserToDo object.

Edit 2: After docmanhattan answer here is the code:

UserToDoMap:

public class UserToDoMap : ClassMap<UserToDo>
{
    public const string TableName = "tblToDoList";
    public const string c_userID = "tdlUserID";
    public const string c_missionID = "tdlMissionID";
    public const string c_rank = "tdlRank";

    public UserToDoMap()
    {
        Table(TableName);
        CompositeId<UserToDoId>(x => x.ID)
            .KeyReference(x => x.UserIdPart, c_userID)
            .KeyReference(x => x.MissionIdPart, c_missionID);
        References(x => x.ID.UserIdPart).Column(c_userID).Cascade.All().UniqueKey(DBConsts.UniqueKeys.ToDoList_UserRanks).Not.Nullable();
        References(x => x.ID.MissionIdPart).Column(c_missionID).Cascade.All().Not.Nullable();
        Map(x => x.Rank).Column(c_rank).UniqueKey(DBConsts.UniqueKeys.ToDoList_UserRanks).Not.Nullable();
    }
}

UserToDoId entity:

public class UserToDoId
{
    public virtual User UserIdPart { get; set; }
    public virtual Mission MissionIdPart { get; set; }

    public override bool Equals(object obj)
    {
        UserToDoId recievedObject = (UserToDoId)obj;

        if ((UserIdPart.ID == recievedObject.UserIdPart.ID) &&
            (MissionIdPart.ID == recievedObject.MissionIdPart.ID))
        {
            return (true);
        }

        return (false);
    }

    public override int GetHashCode()
    {
        return base.GetHashCode();
    }
}

UserToDo entity:

public class UserToDo
{
    public virtual UserToDoId ID { get; set; }

    public virtual int Rank { get; set; }

    public UserToDo()
    {

    }

    public override bool Equals(object obj)
    {
        UserToDo recievedObject = (UserToDo)obj;

        if ((ID.UserIdPart.ID == recievedObject.ID.UserIdPart.ID) &&
            (ID.MissionIdPart.ID == recievedObject.ID.MissionIdPart.ID) &&
            (Rank == recievedObject.Rank))
        {
            return (true);
        }

        return (false);
    }

    public override int GetHashCode()
    {
        return base.GetHashCode();
    }
}

I'm getting the exception when i'm trying to create the session factory:

{"Could not find a getter for property 'UserIdPart' in class 'DatabaseEntities.UserToDo'"}

Ben
  • 63
  • 1
  • 6
  • 1
    What do you mean you can't save? Is there an error? Nothing is saved? Tell us what is happening. – Vadim Aug 15 '11 at 17:30
  • I'm not getting anything. No exception is thrown but the row isn't added to the DB. BTW, I'm using the save method on UserToDo object. – Ben Aug 15 '11 at 18:01
  • Have you overridden the Equals and GetHashCode methods on your UserToDo type? – docmanhattan Aug 15 '11 at 19:53
  • are you able to get anything from database? i mean create line. and access it. Also i would look in trace / console of yours what sql is generated – cpoDesign Aug 15 '11 at 20:42
  • Yes, I'm able to add regular entities and select them from the database. And as I said, when it comes to the selecting it is possible for UserToDo. – Ben Aug 15 '11 at 21:33
  • Remove the explicit references to the composite id parts (References(x => x.ID.UserIdPart... etc.) Also, I've added what your Equals and GetHashCode methods should look like. – docmanhattan Aug 15 '11 at 22:15

1 Answers1

5

I've had lots of problems with doing things with composite ids, such as this. I'd suggest doing what I did which is create a new type that just encompasses what the composite id uses for the id and then mapping it like so:

CompositeId<UserToDoId>(x => x.ID)
    .KeyReference(x => x.UserIdPart, c_userID)
    .KeyReference(x => x.AchievementIdPart, c_missionID);

Where UserToDoId has the two references used in the composite id:

public class UserToDoId
{
    User UserIdPart { get; set; }
    Achievement AchievementIdPart { get; set; }

    public override bool Equals(object obj)
    {
        return Equals(obj as UserToDoId);
    }

    private bool Equals(UserToDoId other)
    {
        if (ReferenceEquals(other, null)) return false;
        if (ReferenceEquals(other, this)) return true;

        return UserIdPart.ID == other.UserIdPart.ID &&
            MissionIdPart.ID == other.MissionIdPart.ID;
    }

    public override int GetHashCode()
    {
        unchecked
        {
            int hash = GetType().GetHashCode();
            hash = (hash * 31) ^ UserIdPart.ID.GetHashCode();
            hash = (hash * 31) ^ MissionIdPart.ID.GetHashCode();

            return hash;
        }
    }
}

I have no idea why, but there are a bunch of little problems that pop up when you don't use another type to hold the pieces of the component id.

One problem I had was linked at the beginning on my answer. Another one I had was using a composite id for a parent abstract class with subclass mappings would attempt to create instances of the abstract class (which you can't do) for certain queries. Implementing this new type fixed both of these problems.

Give it a try and see if that works. Also, 'achievment' is spelled 'achievement' (not trying to taunt, I just hope you'll avoid people snickering at your code cause of a spelling error :-D)

Community
  • 1
  • 1
docmanhattan
  • 2,368
  • 1
  • 25
  • 28
  • I've tried what you suggested and it still doesn't work. I'll add the new code in the question. – Ben Aug 15 '11 at 21:32