I have an NHibernate map which defines a HasMany-relationship on a type, i.e. a class has a list of another class.
I would like NHibernate to be able to read uncommitted data including the list resulting from the HasMany-relationship.
I have isolationlevel ReadUncomitted and I am able to write data and read it back before committing.
However, the list is always empty, unless I commit first.
Is there a way to make NHibernate populate objects with data from HasMany-relationships?
EDIT
It turns out any non-primitive types on a class are not populated by an uncommitted read unless they are actually added to the class.
For example, class Foo
below has a list of Member
which - in the database - are connected by Id
. If I persist an instance of Foo
and an instance of Member
to the database using NHibernate and both are in fact related by the value of Id
, then Foo
will not have the expected instance of Member
if I read it back uncommitted (i.e. before completing the transaction).
public class Member
{
Guid Id{ get; set; }
}
public class Foo
{
List<Member> MemberList{ get; set; }
}
// the mapping
public class FooMap
{
HasMany(x => x.MemberList).KeyColumn("Id").PropertyRef("Id");
}
However, If I create an instance of Foo
and an instance of Member
and set the latter as a reference of the former and persist both to the database using NHibermate, then Foo
will have the expected instance of Member
when I read it back before completing the transaction.
If I complete the transaction then Foo
will have the expected instance of Member
on subsequent reads; as long as the transaction is completed it is irrelevant whether Member
existed only as a database record with the correct FK to Foo
or it was a reference to Foo
.
Revised Question:
It is possible have NHibernate populate complex members based on FK-relationships only during an uncommitted read?
There seems to be some general confusion here, so to clarify a few things:
The isolation level of a transaction only affects what changes from other
transactions are visible. A transaction can always read back data it has modified itself.
If follows that the call to Commit() for the transaction has nothing to with whether
or not the NHibernate session that owns the transaction can read back those changes
or not. It always can.
However, NHibernate will not modify an already loaded instance. You, as the developer
of the model, is responsible for making changes in loaded entities, making sure the model
is still valid and consistent with your business rules, and then NHibernate will
store the changes in the DB. It is not NHibernate's job to make a part of you model
consistent with changes you made in another part.
Reading back before or after the call to Commit() doesn't matter, as NHibernate
guarantees that a session will only contain at most one copy of each entity. So when
you query for the object you just saved, you will just get back the same, already
loaded, unmodified instance.
What matters is if you close the session and open a new session for your query. Then
NHibernate will instantiate the object again, and it will reflect the current state
of the database. The same effect can be had in the original session if you use
Clear() or Evict() on the session (after having called Flush() or Commit()). These
methods remove the already loaded instance from the session, so the next time you
query for it, the session will create a new instance, which would then of course reflect the current state of the database, as visible from the current transaction.
When you instantiate a new object it naturally contains default values for all it's properties + whatever is set through the constructor.
This means you must query for your entity object in order to get any persistent data in it.
So, e.g. if we have these two classes
public class Customer
{
public virtual Guid Id { get; set; }
public virtual ICollection<Order> Orders { get; set; }
}
public class Order
{
public virtual Guid Id { get; set; }
public virtual string Description { get; set; }
//todo add items
}
Then we would need such a method:
public Customer GetCustomerWithUncommitedOrders(Guid customerId)
{
ITransaction t = null;
Customer customer = null;
try
{
t = session.BeginTransaction(System.Data.IsolationLevel.ReadUncommitted);
customer = session.QueryOver<Customer>().Where(x => x.Id == customerId).Fetch(x => x.Orders).Eager.List();
t.Commit();
}
catch (Exception ex)
{
//todo log
if (t != null)
t.Rollback();
}
finally
{
if (t != null)
t.Dispose();
}
return customer;
}
We can then edit the customer, and save it in another (or the same, depending on "editing" duration) transaction.
However, this probably brings up concurrency worries.