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

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;
}
}

Related

How to map an abstract property with NHibernate union subclass?

Referring to Ayende's post here:
http://ayende.com/blog/3941/nhibernate-mapping-inheritance
I have a similar situation that can be reached by extending the union-subclass mapping of the above post a bit, by adding an abstract Name-property to the Party. The model would be as follows:
public abstract class Party
{
public abstract string Name { get; }
}
public class Person : Party
{
public override string Name { get { return this.FirstName + " " + this.LastName; } }
public virtual string FirstName { get; set; }
public virtual string LastName { get; set; }
}
public class Company : Party
{
public override string Name { get { return this.CompanyName; } }
public virtual string CompanyName { get; set; }
}
I'm looking for a mapping that would allow me to query over the parties in the following manner:
session.QueryOver<Party>().Where(p => p.Name.IsLike("firstname lastname")).List();
The mapping I'm using:
<class name="Party" table="`party`" abstract="true">
<id access="backfield" name="Id">
<column name="Id" />
<generator class="sequence">
<param name="sequence">party_id_seq</param>
</generator>
</id>
<union-subclass name="Person" table="`person`">
<property name="Name" formula="first_name || ' ' || last_name" update="false" insert="false" access="readonly">
</property>
<property name="FirstName">
<column name="first_name" />
</property>
<property name="LastName">
<column name="last_name" />
</property>
</union-subclass>
<union-subclass name="Company" table="`company`">
<property name="Name" access="readonly" update="false" insert="false">
<column name="company_name" />
</property>
<property name="CompanyName">
<column name="company_name" />
</property>
</union-subclass>
For both
session.QueryOver<Person>().Where(p => p.Name.IsLike("firstname lastname")).List();
and
session.QueryOver<Company>().Where(p => p.Name.IsLike("companyName")).List();
this behaves as I'd expect, and I can query over the name and get the matching results. However, when I do
session.QueryOver<Party>().Where(p => p.Name.IsLike("firstname lastname")).List();
The query doesn't match the Persons at all, but uses the mapping from the union-subclass of the company. So when parametrized with Party, the query seems to be essentially the same as when parametrized with Company (the WHERE-clause of the query is: WHERE this_.company_name = ((E'firstname lastname')::text))
Any pointers about where I might be going wrong and how to achieve what I'm after?
It would be because you were using the logic inside the properties, which NHibernate would be unable to determine. Since you have already defined the formulas for the Name field it's best to keep them as standard properties. So if you correct the entities as follows, it should work
public abstract class Party
{
public abstract string Name { get; }
}
public class Person : Party
{
public virtual string Name { get; set; }
public virtual string FirstName { get; set; }
public virtual string LastName { get; set; }
}
public class Company : Party
{
public virtual string Name { get; set; }
public virtual string CompanyName { get; set; }
}
Apparently this is a known issue of NHibernate 3.x:
https://nhibernate.jira.com/browse/NH-2354?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel

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 ;
}

NHibernate Stale State Issue

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.

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);
}
}