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 close开发者_运维知识库d 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.
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
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.
精彩评论