NHibernate Stale State Issue - nhibernate

I'm curious if anyone could help me resolve an issue of stale state in nHibernate.
First, the .Net class code:
public class Test
{
public static Test Get(int testId) { return Factory.GetTest(testId); }
public Test() { Related = new List<TestRelate>(); }
public virtual int ID { get; protected set; }
public virtual string Name { get; set; }
public virtual IList<TestRelate> Related { get; set; }
public virtual void Delete() { Factory.Delete(this); }
public virtual void Save() { Factory.Save(this); }
}
public class TestRelate
{
protected TestRelate() { }
public TestRelate(Test test) { TestID = test.ID; }
public virtual int ID { get; protected set; }
public virtual int TestID { get; set; }
public virtual string Data { get; set; }
public virtual void Delete() { Factory.Delete(this); }
public virtual void Save() { Factory.Save(this); }
}
class Factory
{
public static Test GetTest(int testId)
{
ISession session = Session.HybridSessionBuilder.Instance;
IList<Test> ret = null;
ITransaction tx = null;
tx = session.BeginTransaction();
ret = session.CreateCriteria(typeof(Test))
.Add(Expression.Eq("ID", testId))
.List<Test>();
tx.Commit();
return ret.Count == 0 ? null : ret[0];
}
public static void Save<T>(T element)
{
ISession session = Session.HybridSessionBuilder.Instance;
ITransaction tx = null;
tx = session.BeginTransaction();
session.Save(element);
tx.Commit();
}
public static void Delete<T>(T element)
{
ISession session = Session.HybridSessionBuilder.Instance;
ITransaction tx = null;
tx = session.BeginTransaction();
session.Delete(element);
tx.Commit();
}
}
Then the nHibernate mapping XML:
<class name="Data.Test.Test, Data" table="test_info">
<id name="ID" column="testid">
<generator class="native" />
</id>
<property name="Name" />
<bag name="Related" table="test_relate" lazy="false" cascade="none">
<key column="testid"></key>
<one-to-many class="Data.Test.TestRelate, Data"></one-to-many>
</bag>
</class>
<class name="Data.Test.TestRelate, Data" table="test_relate">
<id name="ID" column="relateid">
<generator class="native" />
</id>
<property name="TestID" />
<property name="Data" />
</class>
And finally the code I'm having trouble with:
Data.Test.Test Test = new Data.Test.Test();
Test.Name = "Hello World";
Test.Save();
Data.Test.TestRelate Relate = new Data.Test.TestRelate(Test);
Relate.Data = "How are you?";
Relate.Save();
Test = Data.Test.Test.Get(Test.ID);
int Count = Test.Related.Count;
The problem is that the Test.Related list is always empty when I run this code. However if I destroy the NHibernate session and load up the Test again it populates the list as expected. I realize I could probably flush all caching data but it seems like there should be a cleaner solution to this issue. Any suggestions?

When you do new Data.Test.TestRelate(Test) there is nothing that adds the new TestRelate instance to the collection in the owner Test. (Unless you do that in the constructor, but I assume you only set TestId there).
You should Add() the new TestRelate instance to Test.Related. Nhibernate will notice the change in the collection and save the new item when the session is flushed.

NHibernate doesn't populate one-to-many collections automatically on commit. You should simply add TestRelate instances to the Related list, as you would do without NHibernate, and then (if you set a "cascade save" mapping) even commit the Test instance only.
There is no need to use the TestID property inside the program at all, as this property is actually only a byproduct of relational DB mapping.

Alright so I realized that my approach was due to some past failed attempts at utilizing NHibernate's cascading. I'll go over each one of the issues and what I did to resolve it.
If I set up cascading saves NHibernate would fail when I would try to add Related elements to a new Test element because the TestID value is not allowed to be null in the database. Altering the property from an integer type to the Test type itself remedied this situation as NHibernate was able to populate the field value after saving the new Test element.
Attempting to delete a Related record by removing it from the list would result in an error due to NHibernate attempting to Update the TestID field to null prior to a delete. Adding the inverse="true" attribute to the Bag mapping element resolved this issue.
Deleting the Test object would not delete the orphaned Related records. Setting the cascade attribute to all-orphan-delete remedied this.
Here's all the new code (there were no changes to the Factory class):
public class Test
{
public static Test Get(int testId) { return Factory.GetTest(testId); }
public Test() { Related = new List<TestRelate>(); }
public virtual int ID { get; protected set; }
public virtual string Name { get; set; }
public virtual IList<TestRelate> Related { get; set; }
public virtual void Delete() { Factory.Delete(this); }
public virtual void Save() { Factory.Save(this); }
}
public class TestRelate
{
protected TestRelate() { }
public TestRelate(Test test) { Test = test; }
public virtual int ID { get; protected set; }
public virtual Test Test { get; set; }
public virtual string Data { get; set; }
public virtual void Delete() { Factory.Delete(this); }
public virtual void Save() { Factory.Save(this); }
}
Mapping changes:
<class name="Data.Test.Test, Data" table="test_info">
<id name="ID" column="testid">
<generator class="native" />
</id>
<property name="Name" />
<bag name="Related" table="test_relate" lazy="false" cascade="all-delete-orphan" inverse="true">
<key column="testid"></key>
<one-to-many class="Data.Test.TestRelate, Data"></one-to-many>
</bag>
</class>
<class name="Data.Test.TestRelate, Data" table="test_relate">
<id name="ID" column="relateid">
<generator class="native" />
</id>
<many-to-one name="Test" column="testid" />
<property name="Data" />
</class>
The following code now behaves as expected:
Data.Test.Test Test;
Data.Test.TestRelate Relate;
Test = new Data.Test.Test();
Test.Name = "Hello World";
Relate = new Data.Test.TestRelate(Test);
Relate.Data = "How are you?";
Test.Related.Add(Relate);
Test.Save();
Relate = new Data.Test.TestRelate(Test);
Relate.Data = "Relate #2";
Test.Related.Add(Relate);
Test.Save();
Test.Related.RemoveAt(0);
Test.Save();
Test = Data.Test.Test.Get(Test.ID);
int Count = Test.Related.Count;
Test.Delete();
I was able to glean most of these answers from http://ayende.com . I highly recommend this site as a resource for nHibernate questions.

Related

NHibernate - Get parent with paged child collection

I'm currently working on writing a very basic online forum, and I want to retrieve a thread with a child collection of paged posts. So my mappings are:
<class name="Thread" table="ForumThreads">
<id name="Id">
<generator class="identity"></generator>
</id>
<property name="Title"></property>
<bag name="Posts">
<key column="ThreadID"></key>
<one-to-many class="Post"/>
</bag>
</class>
<class name="Post" table="ForumPosts">
<id name="Id">
<generator class="identity"></generator>
</id>
<property name="Content"></property>
<many-to-one name="Thread"
class="Thread"
column="ThreadID">
</many-to-one>
</class>
And I want to do something like this:
public class Thread
{
public virtual int Id { get; set; }
public virtual string Title { get; set; }
public virtual IEnumerable<Post> Posts { get; set; }
}
public class Post
{
public virtual int Id { get; set; }
public virtual Thread Thread { get; set; }
public virtual string Content { get; set; }
}
public Thread GetThread(int threadId, int page, int pageSize, out int count)
{
var session = SessionFactory.CurrentSession;
// Query to get the thread with a child collection of paged posts.
return thread;
}
Is it possible to do this with one query, or am I going to have to split it in to two?
Thanks
var threadId = ...
session.QueryOver<Thread>
.Where(thread => thread.Id == threadId)
.Fetch(thread => thread.Posts).Eager
.Take(pageSize)
.Skip(page)
.TransformUsing(Transformers.DistinctRootEntity)
.List<Thread>();
If you are thinking of doing db level paging, then the approach would be something like this,
You have to use the collection to support lazy loading with extra mode
You may need to use a filter to load the collection, (but you cannot return thread as the result as if you try to access the collection it will load all posts)
public List<Post> GetThreadPosts(int threadId, int page, int pageSize, out int count)
{
var session = SessionFactory.CurrentSession;
Thread thread = (Thread )session.Get(typeof(Thread ), threadId);
var posts = session.CreateFilter(thread .Posts, "").SetFirstResult((page - 1) * pageSize).SetMaxResults(pageSize).List();
return posts ;
}

When using DTOs, Automapper & Nhibernate reflecting changes in child collections of DTO in domain object being updated

I'm not massively familiar with this design but I am hoping to get some guidance.
I have a backend service that sends out DTOs to a WPF smart client. On the WPF smart client the user will change,delete and modify items and then the changes are sent back (client --> server). As an example, currently I am working on the Customer details form and the user has the ability to add,remove and change categories belonging to a customer in a datagrid. When the DTO is sent back to the server I would like to load in the domain object that is related to the ID in the DTO and apply the changes made on the DTO to the domain object, including all the child collections.
I have made an attempt at doing something similar to this in the code below with the UpdateCustomer method. However, I think I am way off the mark. When the code runs instead of ending up with a list of {Individual,Company,NGO,Government} I end up with a list of {Individual,B2B,Company,NGO,Government} as it has clearly not deleted the B2B entry from the original list.
One option that has occurred to me is to loop through the DTO collection and compare it to the collection from the domain object and add, remove and update dependent on what has been modified. However, this seemed really cumbersome.
What do I need to do to apply the changes from the DTO to the child collections in my domiain object?
Thank you very much for any assistance it will be thoroughly appreciated
Alex
public class Customer
{
public virtual int Id { get; set; }
public virtual IList<Category> Categories { get; private set; }
public virtual string Code { get; set; }
public virtual string Description { get; set; }
public Customer()
{
Categories = new List<Category>();
}
public virtual void AddCategory(string categoryName)
{
Categories.Add(new Category(categoryName));
}
}
public class Category
{
public virtual string CategoryName { get; private set; }
public virtual Customer Customer {get;set;}
public virtual int Id { get; set; }
protected Category(){}
public Category(string name)
{
CategoryName = name;
}
}
}
public void SetUpAutoMapper()
{
Mapper.CreateMap<Category, CategoryDto>();
Mapper.CreateMap<Customer, CustomerDto>();
Mapper.CreateMap<CategoryDto, Category>();
Mapper.CreateMap<CustomerDto, Customer>();
Mapper.AssertConfigurationIsValid();
}
public void SaveCustomer()
{
var customer = new Customer{Code="TESTCUST",Description="TEST CUSTOMER"};
customer.AddCategory("Individual");
customer.AddCategory("B2B");
customer.AddCategory("Healthcare");
customer.AddCategory("NGO");
repository.Save(customer);
}
public CustomerDto GetCustomer(int customerId)
{
var customer = repository.GetCustomer(customerId);
var customerDto = Mapper.Map<Customer,CustomerDto>(customer);
return customerDto;
}
public void UpateCustomer(CustomerDto customerToUpdate)
{
/*imagine that the dto incoming has had the following operations performed on it
-----add new category----
customerToUpdate.Categories.Add(new CategoryDto {CategoryName = "Government"});
---update existing category---
customerToUpdate.Categories[2].CategoryName = "Company";
---remove category---
customerToUpdate.Categories.RemoveAt(1);*/
var customer = repository.GetCustomer(customerToUpdate.Id);
/* How in this bit do I ensure that the child collection changes are
propogated into the underlying customer object retrieved from the database*/
var customer = Mapper.Map<CustomerDto,Customer>(customerToUpdate);
repository.Save(customer);
}
public class CustomerDto
{
public int Id { get; set; }
public string Code { get; set; }
public string Description { get; set; }
public List<CategoryDto> Categories { get; set; }
}
public class CategoryDto
{
public int Id { get; set; }
public string CategoryName { get; set; }
}
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2">
<class name="Customer" table="Customer">
<id name="Id" column="CustomerId">
<generator class="native"/>
</id>
<property name="Code" />
<property name="Description" />
<bag name="Categories" table="Categories" cascade="all" inverse="false">
<key column="FK_CustomerID" />
<one-to-many class="Category"/>
</bag>
</class>
<class name="Category" table="Categories">
<id name="Id" column="CategoryId">
<generator class="native"/>
</id>
<many-to-one name="Customer" column="FK_CustomerId" not-null="true" class="Customer"></many-to-one>
<property name="CategoryName" />
</class>
</hibernate-mapping>
I recently did something similar but with EF as the datatier. I don't know nhibernate to know if the same approach would work.
Basic steps were
Ensure the destination collection is loaded from db and attached to the object graph for change tracking
.ForMember(dest => dest.Categories, opt => opt.UseDestinationValue())
Then create a custom IObjectMapper for mapping IList<> to IList<T> where T : Entity
The custom IObject mapper used some code from http://groups.google.com/group/automapper-users/browse_thread/thread/8c7896fbc3f72514
foreach (var child in source.ChildCollection)
{
var targetChild = target.ChildCollection.SingleOrDefault(c => c.Equals(child)); // overwrite Equals or replace comparison with an Id comparison
if (targetChild == null)
{
target.ChildCollection.Add(Mapper.Map<SourceChildType, TargetChildType>(child));
}
else
{
Mapper.Map(child, targetChild);
}
}
Finally one last piece of logic to check all Id's in targetCollection exist in sourceCollection and delete them if they don't.
It wasn't all that much code in the end and is reusable in other actions.
Mapper.CreateMap<Customer, CustomerDto>()
.ForMember(dest => dest.Categories, opt => opt.MapFrom(src =>src.Categories));
or
Mapper.CreateMap<IList<Category>, IList<CategoryDto>>();
something like this to tell automapper to map the list, too.

NHibernate mapping does not populate the bag

<class name="CashThreshold" table="CASH_THRESHOLD_COUNTERS" lazy="true" >
<id name="Id" column="ID" >
<generator class="assigned" />
</id>
<bag name="ThresholdNominalsList" cascade="all" inverse="true" lazy="false" table="CASH_THRESHOLD_CAS_COUNTERS">
<key column="CASH_THRESHOLD_ID" />
<one-to-many class="NominalThreshold" />
</bag>
Map second table
<class name="NominalThreshold" table="CASH_THRESHOLD_CAS_COUNTERS" lazy="true" >
<composite-id>
<key-property name="CashTrasholdId" column="CASH_THRESHOLD_ID" type="long"></key-property>
<key-property name="Nominal" column="NOMINAL" type="long"></key-property>
</composite-id>
<property name="MinNoteCount" column="MIN_NOTE_COUNT" />
<property name="MaxNoteCount" column="MAX_NOTE_COUNT" />
Table classes
public class CashThreshold : ICashThreshold
{
public virtual long Id { set; get; }
/// !!!!!!! IS ALWAYS AMPTY, but not null !!!!!
public virtual IList<INominalThreshold> ThresholdNominalsList { set; get; }
}
public class NominalThreshold : INominalThreshold
{
public virtual long CashTrasholdId { set; get; }
public virtual long Nominal { set; get; }
public virtual long MinNoteCount { set; get; }
public virtual long MaxNoteCount { set; get; }
public override bool Equals(Object obj)
{
var tmp = (INominalThreshold)obj;
return (tmp.CashTrasholdId == CashTrasholdId && tmp.Nominal == Nominal);
}
public override int GetHashCode()
{
return (int)CashTrasholdId ^ (int)Nominal;
}
}
Function for getting list of ICashThreshold
ICriteria selectAll = currentSession.CreateCriteria<ICashThreshold>();
IList<ICashThreshold> list = selectAll.List<ICashThreshold>();
Query executed whith no errors. Bag-query executed successfully in sql-client and returned 4 result, but IList< INominalThreshold > ThresholdNominalsList has no elements.
Thanks.
Problem solved. NHibernate mapped bag successfully, but list was empty, because the data in DB was NOT COMMITTED. I inserted test data in the table, but did not commit it. When I execute query in sql-client, it executed successfully(because do it in session, where table rows inserted), but hibernate had another session. Thats why NHibernate could not see the table data.

Nhibernate mapping

I am trying to map Users to each other. The senario is that users can have buddies, so it links to itself
I was thinking of this
public class User
{
public virtual Guid Id { get; set; }
public virtual string FirstName { get; set; }
public virtual string LastName { get; set; }
public virtual string EmailAddress { get; set; }
public virtual string Password { get; set; }
public virtual DateTime? DateCreated { get; set; }
**public virtual IList<User> Friends { get; set; }**
public virtual bool Deleted { get; set; }
}
But am strugling to do the xml mapping.
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
assembly="MyVerse.Domain"
namespace="MyVerse.Domain" >
<class name="User" table="[User]">
<id name="Id">
<generator class="guid" />
</id>
<property name="FirstName" />
<property name="LastName" />
<property name="EmailAddress" />
<property name="Password" />
<property name="DateCreated" />
<property name="Deleted" />
<set name="Friends" table="UserFriend">
<key foreign-key="Id"></key>
<many-to-many class="User"></many-to-many>
</set>
</class>
</hibernate-mapping>
something like
<bag name="Friends" table="assoc_user_table" inverse="true" lazy="true" cascade="all">
<key column="friend_id" />
<many-to-many class="User,user_table" column="user_id" />
</bag>
Consider using the repository pattern. Create a Repository contract and a base abstract class that takes one of your entities as a type (your mapped class)
Open the session when the repository is initialized and close when destroyed. (implement IDisposable).
Then make sure all of your access to the session happens within the using statement:
[pseudo-code]:
using(var repository = RepositoryFactory<EntityType>.CreateRepository())
{
var entity = repository.get(EntityID);
foreach (somesubclass in entity.subclasscollection)
{
//Lazy loading can happen here, session is still open with the repository
... Do Something
}
}
I use a base abstract class for my Repositories. This one is for my readonly repository but you'll get the drift. They key is to keep your units of work small, open the session only when you have something to do with the database, then let it close on the dispose. Here's the base class, disclaimer YMMV:
public interface IEntity
{
int Id { get; set; }
}
public interface IRORepository<TEntity> : IDisposable where TEntity : IEntity
{
List<TEntity> GetAll();
TEntity Get(int id);
}
public abstract class RORepositoryBase<T> : IRORepository<T> where T : IEntity
{
protected ISession NHibernateSession;
protected RORepositoryBase()
{
NHibernateSession = HibernateFactory.OpenSession();
NHibernateSession.DefaultReadOnly = true;
}
public ISession Session { get { return NHibernateSession; } }
public void Dispose()
{
NHibernateSession.Flush();
NHibernateSession.Close();
NHibernateSession.Dispose();
}
public virtual List<T> GetAll()
{
return NHibernateSession.Query<T>().ToList();
}
public virtual T Get(int id)
{
return NHibernateSession.Get<T>(id);
}
}

Querying Overriding Entities Using a Self Join and the NHibernate Criteria API

I have a simple Waiver model, and I would like to make a query that returns all the Waivers that are not overridden.
public class Waiver
{
private readonly int id;
protected Waiver()
{
this.id = 0;
}
public virtual int Id { get { return id; } }
public virtual string Name { get; set; }
public virtual string Description { get; set; }
public virtual bool IsRequired { get; set; }
public virtual DateTime EffectiveDate { get; set; }
public virtual Waiver OverriddenWaiver { get; set; }
}
Here's the map:
<class name="Waiver" table="Music_Waivers">
<id name="id" access="field" column="WaiverId" unsaved-value="0">
<generator class="native" />
</id>
<property name="Name" column="Name" />
<property name="Description" column="Description" />
<property name="IsRequired" column="IsRequired" />
<property name="EffectiveDate" column="EffectiveDate" />
<many-to-one name="OverriddenWaiver" class="Waiver" column="OverrideWaiverId" />
</class>
Now I want to have a method in my Repository with the signature public IList GetLatest(). For some reason I'm having a hard time implementing this with the CriteriaAPI. I can write this in T-SQL no problem.
I ended up brute forcing a solution. It's not pretty, but since I know the table will be tiny (probably going to end up being only 5 rows) I came up with the following code solution:
public IList<Waiver> GetLatest()
{
using (var session = SessionManager.OpenSession())
{
var criteria = session.CreateCriteria(typeof (Waiver));
var waivers = criteria.List<Waiver>();
var nonOverridenWaivers = new List<Waiver>();
foreach(var waiver in waivers)
{
bool overrideExists = waivers.Any(w => w.Overrides != null &&
w.Overrides.Id == waiver.Id);
if (!overrideExists)
nonOverridenWaivers.Add(waiver);
}
return nonOverridenWaivers;
}
}