1

I've got a problem with NHibernate and, as I suppose, with lazy load.

I've got two entity classes, that are related to each other:

public class User
{
    // lots of properties
    public virtual Role Role { get; set; }
}

public class Role
{
    // properties
    public virtual IList<User> UsersInRole { get;set; }
    public Role()
    {
        this.UsersInRole = new List<User>();
    }
}

Relation is One-To-Many (One role - Many users). Classes are mapped with Fluent as following:

public class UserMapping : ClassMap<Models.Accounts.User>
{
    public UserMapping() 
    {
        this.Table("Users");
        this.Id(u => u.ID).GeneratedBy.Native();
        //properties mapping            
        this.References<Models.Accounts.Role>(u => u.Role);
    }
}

public class RoleMapping : ClassMap<Models.Accounts.Role>
{
    public RoleMapping()
    {
        this.Table("Roles");
        this.Id(r => r.ID).GeneratedBy.Native();
        // properties mapping 
        this.HasMany<User>(r => r.Users).AsBag().Inverse().KeyColumn("Role_id");
    }
}

When I'm creating a Role, and assign some User to it - Role::Users property is null until session is closed and another one is opened. So, consider following code:

object Foo()
{
        var session = FluentManager.OpenNewSession();
        var t1 = session.BeginTransaction();
        Role role = new Role("admin");
        session.SaveOrUpdate(role);
        t1.Commit();

        var t2 = session.BeginTransaction();
        session.SaveOrUpdate(new User() { Login = "log1", Role = role });
        t2.Commit();            

        Role oldRole = session.Get<Role>((uint)1);
        return oldRole.Users; // null here
}

On the other hand, following code works fine:

object Foo()
{
        var session = FluentManager.OpenNewSession();
        var t1 = session.BeginTransaction();
        Role role = new Role("admin");
        session.SaveOrUpdate(role);
        t1.Commit();

        var t2 = session.BeginTransaction();
        session.SaveOrUpdate(new User() { Login = "log1", Role = role });
        t2.Commit();
        session.Close();

        var session2 = FluentManager.OpenNewSession();
        Role oldRole = session2.Get<Role>((uint)1);
        return oldRole.Users; // Actual list of users
}

Though, I'd like to work with collection of users in a given role w/out session close. What I did wrong? Many thanks in advance.

PS: I'm using Fluent NHibernate 1.2 with SQLite database, if that matters.

3
  • Do you mean oldRole.UsersInRole? Is is really null, not just empty? Commented Apr 19, 2011 at 16:58
  • @Stefan, yes, I ment oldRole.UsersInRole. And yes, it is null, not empty. Commented Apr 20, 2011 at 6:13
  • @Stefan, My guess is he's not initializing the collection in the constructor he's not showing us Role(string roleName). This is why the collection is null and not empty. Commented Apr 22, 2011 at 14:13

2 Answers 2

2

The reason why role.Users is null in the first example is because you don't manually take care of this relationship. You need to maintain the bidirectional relationship yourself. In your first example it is not getting your Role object from the database. It's getting it from Session. In your 2nd example you close your session and open a new one. This forces it to read the information from the database and your relationships will be intact at this point. Here is what I might do to correct this:

public class User
{
    // lots of properties
    public virtual Role Role { get; set; }
}

public class Role
{
    // properties
    public virtual IList<User> UsersInRole { get;set; }

    public Role()
    {
        this.UsersInRole = new List<User>();
    }

    public void AddUser(User user)
    {
        if(UsersInRole.Contains(user))
            return;

        user.Role = this;
        UsersInRole.Add(user);
    }
}

Your example would now look something like this:

object Foo()
{
        var session = FluentManager.OpenNewSession();
        var t1 = session.BeginTransaction();
        Role role = new Role("admin");
        session.SaveOrUpdate(role);
        t1.Commit();

        var t2 = session.BeginTransaction();
        User newUser = new User();
        newUser.Login = "log1";

        //This takes care of our relationships
        role.AddUser(newUser);

        session.SaveOrUpdate(newUser);
        t2.Commit();            

        Role oldRole = session.Get<Role>((uint)1);
        return oldRole.Users; // This should be fine here since we used AddUser
}

You can also take a look at the following for more detail:
Getting ManyToMany relationship to load

Sign up to request clarification or add additional context in comments.

Comments

0

Some shots in the dark:

Have you tried accessing role.Users instead of oldRole.Users?

Have you tried removing the constructor of Role?

Have you tried getting the role using a detached criteria or QueryOver rather than Get? Maybe you're getting the wrong role.

2 Comments

Dear Ilya, thank you for your reply. Accessing role.UsersInRole instead of oldRole.UsersInRole gives the same effect (null). I didn't try to remove the constructor yet, though I tried to remove collection initialization from it - same effect. And at last - I get correct role, when using Get`. Because, there is only one role in the database and all other properties are initialized correctly.
Just tried to remove the constructor. Works the same - collection is null, until the session is closed, and another one is opened. Also tried to make session.Flush() before and even after transaction.Commit(). The behavior is the same.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.