vbullinger
vbullinger

Reputation: 4266

Create Custom Criterion in NHibernate?

I'm still a bit of a n00b when it comes to NHibernate. Let's say I have the following:

var myCriteria = this.Session.CreateCriteria(typeof(SomeModel))
                         .Add(Restrictions.Eq("SomeProperty", someValue);

Then, let's say I want to add criteria in a way that's reusable. Meaning, I want to make a custom criterion. I'm seeing very, very little information online on this. Specifically, I'd like to turn the following:

var myCriteria = this.Session.CreateCriteria(typeof(SomeModel))
                .Add(Restrictions.Eq("SomeProperty", someValue)
                .CreateAlias("SomeClass", "alias", JoinType.LeftOuterJoin)
                .Add(Restrictions.Eq("alias.SomeOtherProperty", someOtherValue));

Into the following:

var myCriteria = this.Session.CreateCriteria(typeof(SomeModel))
                             .Add(Restrictions.Eq("SomeProperty", someValue)
                             .Add(this.GetAliasCriterion());

Thus extracting

      .CreateAlias("SomeClass", "alias", JoinType.LeftOuterJoin)
             .Add(Restrictions.Eq("alias.SomeOtherProperty", someOtherValue)); 

into a method.

Is this possible? How does this work?

Upvotes: 1

Views: 2195

Answers (3)

viggity
viggity

Reputation: 15237

Personally, I prefer using Linq to NHibernate instead of criteria. It is already included in the latest release of NHibernate, just add "using NHibernate.Linq" to your cs file, as all of the L2H methods are extension methods.

If I want to make a predicate/criteria reusable with L2H, it'll look something like this:

public partial class Person 
{
    //yes, this is kind of ugly, but if I have a complicated predicate that 
    //I want to reuse, I'll make a static method on the entity itself.
    public static Expression<Func<Person, bool>> HasPrimaryRole(string roleCode)
    {
        return p => p.Roles.Any(r => r.RoleCode == roleCode && r.IsPrimary);
    }
}

Usage looks like this:

var session = GetSession();
var midwestSalesManagers = session.Query<Person>()
    .Where(p => p.Region == "Midwest").Where(Person.HasPrimaryRole("RSM"));

or alternatively you could do this:

var midwestSalesManagers = session.Query<Person>()
    .Where(Expression.And(p => p.Region == "Midwest", Person.HasPrimaryRole("RSM")));

I haven't fully tested all of this, but I hope you get my drift. I think it is pretty readable and avoids usage of magic strings so it is refactor friendly.

Upvotes: 1

dotjoe
dotjoe

Reputation: 26940

You could do that with an extension method. I'd be careful though, usually you'll want to see all the alias(joins) that a query is doing.

public static class CriteriaExtensions
{
    public static ICriteria AddSomeClassAliasRestriction(this ICriteria c, object value)
    {
        return c.CreateAlias("SomeClass", "alias", JoinType.LeftOuterJoin)
            .Add(Restrictions.Eq("alias.SomeOtherProperty", value));
    }
}

and use it like...

var myCriteria = this.Session.CreateCriteria(typeof(SomeModel))
.Add(Restrictions.Eq("SomeProperty", someValue)
.AddSomeClassAliasRestriction(someOtherValue);

Upvotes: 1

frictionlesspulley
frictionlesspulley

Reputation: 12438

This is a pattern that I usually use :

Say that I have a filter which adds specific Restrictions on a criteria. (You might even make it an Extension class)

public class DeletedFlagFilter{

  public DetachedCriteria AddFilter(DetachedCriteria criteria)
  {
        criteria.AddRestrictions("Deleted", true);
        return criteria;
  }
}

The caller of the above class say( FindDeletedUserClass ) would then would then use the helper class above to add all the restrictions necessary to define what a deleted user as follows :

   public class FindDeletedUserClass{

      public DetachedCriteria BuildCriteria(){

             var deletedUserCriteria = DetachedCriteria.For<User>();          

             var helper = new DeletedFlagFilter();

             helper.AddFilter(deletedUserCriteria);

             return deletedUserCriteria;

      }
 }

Your separated service layer which actually accesses the NHibernate Session could then conver the DetachedCriteria to criteria connected to a session and execute the same like.

  var myDetachedCriteria = DetachedCriteria.For<SomeModel>();

  var sessionCriteria = myDetachedCriteria.GetExecutableCriteria(Session);

Using the pattern above you would encapsulate the Deleted functionality in the DeletedFlagFilter class. In the future if the definition of Deleted changes the only change would be incurred on the DeletedFlagFilter class

Edit : there is nice tutorial which you could read here

Upvotes: 1

Related Questions