Lambda Expressions with NHibernate.Linq

clock February 19, 2010 07:33 by author jamesstill

In the last two posts below I talked about creating a templated DataService class and also mused about whether UI data queries really were a cross-cutting concern. I'm agnostic about the question really; however, I do have one practical concern. I don't want to expose methods that take any old HQL query that the client sends my way. I'd like to have a controlled way of extending database queries down to the UI.

The answer is LINQ. Thanks to Rahien and others NHibernate.Linq 1.0 RTM is available. (Eventually this will be merged into the NHibernate mainline but for now it's a separate download.) Once you reference the assembly and put a reference to NHibernate.Linq the extension methods are available in your ISession instance:

   1:  // grab an ISession from ISessionFactory            
   2:  var query = from item in session.Linq()                 
   3:              select item;

So I'm going to extend my DataService class below to support LINQ queries along these lines. Kudos to Ryan Lanciaux who blogged on this before me. To support Linq queries we first must modify the IDataService interface to support lamdba expressions:
   1:  using System;
   2:  using System.Collections.Generic;
   3:  using System.Linq;
   4:  using System.Linq.Expressions;
   5:  using System.Text;
   6:   
   7:  namespace ConcertMusic.DomainModel
   8:  {
   9:      public interface IDataService<T>
  10:      {
  11:          T Get(int id);
  12:          IList<T> Get(string hqlQueryString);
  13:          IList<T> Get();
  14:          void AddCriteria(Expression<Func<T, bool>> lambdaFunction);
  15:          T Save(T item);
  16:          bool Delete(T item);       
  17:      }
  18:  }

Now to implement the interface in the DataService class:

   1:  public class DataService<T> : IDataService<T>
   2:      {
   3:          private IList<Expression<Func<T, bool>>> _criteria; 
   4:   
   5:          public DataService() 
   6:          {
   7:             _criteria = new List<Expression<Func<T, bool>>>(); 
   8:          }
   9:   
  10:          public void AddCriteria(Expression<Func<T, bool>> lambdaFunction)
  11:          {
  12:              _criteria.Add(lambdaFunction);
  13:          } 
  14:   
  15:          public IList<T> Get()
  16:          {
  17:              using (ISession session = NHibernateHelper.OpenSession())
  18:              {
  19:                  var query = from item in session.Linq<T>()
  20:                              select item;
  21:   
  22:                  foreach (var criterion in _criteria)
  23:                  {
  24:                      query = query.Where<T>(criterion);
  25:                  }
  26:                  return query.ToList(); 
  27:              }         
  28:          }
  29:   
  30:          // other methods removed for clarity
  31:      }
  32:  }

That's pretty much it for the data access plumbing. Now the client code can fetch the IDataService instance and get back anything it wants using lambda expressions. Let's say I want to get every widget from the database with a name that starts with the letter "V". Here's my code:

   1:  IDataService<Widget> svc = DataServiceFactory<Widget>.Create();
   2:  svc.AddCriteria(item => item.Name.StartsWith("V"));
   3:  IList<Widget> list = svc.Get();

And these lambda expressions can be chained of course for very fine-grained search criteria. Suppose I want every widget with a name that starts with the letter "V", is round and blue, and has greater than 8 teeth:

   1:  IDataService<Widget> svc = DataServiceFactory<Widget>.Create();
   2:  svc.AddCriteria(item => item.Name.StartsWith("V"));
   3:  svc.AddCriteria(item => item.Color.Equals("Blue"));
   4:  svc.AddCriteria(item => item.Shape.Equals("Round"));
   5:  svc.AddCriteria(item => item.Teeth > 8));
   6:  IList<Widget> list = svc.Get();

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5


IComparer and Sorting Widgets

clock January 16, 2008 05:31 by author jamesstill

Back in the turn of the last century, the golden days of .NET 1.1, if you wanted to implement a type-safe custom collection your best bet was to derive from CollectionBase in the System.Collections namespace. CollectionBase was more than just an abstract base class. It was also a wrapper around an internal ArrayList. If you wanted to sort strongly-typed objects in the collection you would implement an IComparer class:

inner class AscendingWidgetSorter : IComparer {
    public int Compare(Object x, Object y) {
        Widget w1 = (Widget)x;
    IComparable ic1 = (IComparable)w1.Size;
    Widget w2 = (Widget)y;
    IComparable ic2 = (IComparable)w2.Size;
    return ic1.CompareTo(ic2);
    }
}

The IComparer implementation could then be passed into a Sort method in your custom collection:

public void Sort() {
    IComparer widgetSorter = new AscendingWidgetSorter();
    InnerList.Sort(widgetSorter);
} 

Fast forward to .NET 2.0/3.5 and we now have support for generics in IComparer which is in the System.Collections.Generic namespace. With the use of templates you no longer have to cast the type inside the Compare method, you can declare the type with the IComparer implementation. Let's say we want to compare square widgets and provide support for sorting them. They come in four sizes labelled A, B, C, and D. Further, if two widgets are of the same size then the larger of the two is the one with the most teeth. A square widget can have anywhere from 4 to 8 teeth. Here's the IComparer implementation:

/// <summary>
/// Widget sizes range from the smallest A through the largest D and teeth 
/// on any particular widget can be as few as 4 and as many as 8. The sort 
/// algorithm must sort from smallest to largest first by size then by the
/// tooth count.
/// </summary>
internal class WidgetSorter : IComparer<SquareWidget> {
    /// <summary>
    /// Returns the smaller of the two widgets where:
    ///     less than 0    - sw1 is smaller than sw2
    ///     0              - sw1 is equal in size to sw2
    ///     greater than 0 - sw1 is greater than sw2 or sw2 is null
    /// </summary>
    /// <param name="sw1"></param>
    /// <param name="sw2"></param>
    /// <returns></returns>
    public int Compare(SquareWidget sw1, SquareWidget sw2) {
        if (sw1.Size.Equals(sw2.Size)) {
            return sw1.Teeth.CompareTo(sw2.Teeth);
        }
        else {
            return sw1.Size.CompareTo(sw2.Size);
        }
    }
}

And of course we need to describe a SquareWidget in our system:

public class SquareWidget {
    private int _id;
    private string _size;
    private int _teeth;
 
    public SquareWidget(int id, string size, int teeth) {
        this._id = id;
        this._size = size;
        this._teeth = teeth;
    }
       
    public int ID {
        get { return this._id; }
    }
 
    public string Size {
        get { return this._size; }
    }
 
    public int Teeth {
        get { return this._teeth; }
    }
}

From there the behavior is the same as before. You create an instance of the WidgetSorter and pass that into the List.Sort method. Here's a sample console app that exercises the code above with a List<T> thrown in for good measure:

class Program {
    static void Main(string[] args) {
 
        List<SquareWidget> widgetList = new List<SquareWidget>();
           
        // load widgets
        widgetList.Add(new SquareWidget(1, "C", 5));
        widgetList.Add(new SquareWidget(2, "A", 4));
        widgetList.Add(new SquareWidget(3, "D", 8));
        widgetList.Add(new SquareWidget(4, "A", 8));
        widgetList.Add(new SquareWidget(5, "D", 7));
        widgetList.Add(new SquareWidget(6, "C", 4));
        widgetList.Add(new SquareWidget(7, "A", 5));
       
        // instantiate our custom widget sorter and do the sort
        IComparer<SquareWidget> widgetSorter = new WidgetSorter();
        widgetList.Sort(widgetSorter);
 
        foreach (SquareWidget sw in widgetList) {
            Console.WriteLine(string.Format(
                "ID: {0} Size: {1} Teeth: {2}", sw.ID, sw.Size, sw.Teeth));
        }
        Console.ReadLine();
    }
}

If you run the app you'll get the list sorted according to our specifications:

ID: 2 Size: A Teeth: 4
ID: 7 Size: A Teeth: 5
ID: 4 Size: A Teeth: 8
ID: 6 Size: C Teeth: 4
ID: 1 Size: C Teeth: 5
ID: 5 Size: D Teeth: 7
ID: 3 Size: D Teeth: 8

Enjoy!

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5


Generics and Nullable Types

clock December 12, 2007 08:50 by author jamesstill

It is often useful in OR mapping to analyze a value type that might be null prior to setting it in a business entity. For instance, SqlDateTime type (in System.Data.SqlTypes namespace) is a value type that is nullable. You can call IsNull to check and Value to retrieve the underlying DateTime type:

SqlDateTime d = new SqlDateTime();
if (d.IsNull) 
    Trace.WriteLine("SqlDateTime is null");
d = DateTime.Now;Trace.WriteLine(string.Format("SqlDateTime is {0}", d.Value.ToString()));

 

This is useful when fetching data from SQLServer. DataSets allow nullable values but strongly-typed classes do not. So if I'm instantiating a business entity, I often call TryParse first to protect against null:

//instantiate SqlCommand cm here
SomeBusinessEntity obj = new SomeBusinessEntity();
DateTime dt = new DateTime();
SqlDataReader dr = cm.ExecuteReader();
if (dr != null) {    
    while (dr.Read()) {        
        obj.ID = (DBNull.Value != dr["ID"]) ? Convert.ToInt32(dr["ID"]) : 0;
        if (DateTime.TryParse(dr["ApprovalDate"].ToString(), out dt))              
            obj.ApprovalDate = dt;
    }
}

 

Wouldn't it be great to have a DateTime struct in C# that allows for null? A bunch of folks have gnashed their teeth over this issue since the release of .NET. And now .NET Framework 3.0 supports nullable value types. But I'm still in 2.0 land and will likely remain there for some time so here's my solution. The goal is to support both the above OR mapping logic via generics and to emulate the nullable feature of the SqlDateTime type. In the end we want to do something like this:

Nullable<DateTime> ndt = new Nullable<DateTime>();
if (Nullable<DateTime>.TryParse(DateTime.Now.ToString(), out ndt))
    obj.ApprovalDate = ndt;

 

We start with a basic generics-enabled struct with exposed properties Value and IsNull:

public struct Nullable<T> {
    private static bool _isnull;
    private T _value;
 
    static Nullable() {
        _isnull = true;
    }
 
    public bool IsNull {
        get { return _isnull; }
    }
 
    public T Value {
        get {
            return _value;
        }
        set {
            _value = value;
            _isnull = false;
        }
    }
}

 

The struct has a private _isnull member which is set to true in the static constructor. Only when the value is set does that flag flip to false. So by default it will be null even if the type is not really null; for instance if the underlying DateTime is initialized at 1/1/0001 or the underlying Int32 is initialized at 0. Now we support the TryParse functionality by adding a delegate function that mimics the signature. Note that I'm not supporting the overloaded method for IFormatProvider but rather keeping it simple:

private delegate bool TryParseDelegate<T>(string s, out T result);

 

Then add a private static function to do the work:

private static bool ParseNullable<T>(string s, out Nullable result, TryParseDelegate Parse) where T : struct {
    if (string.IsNullOrEmpty(s)) {
        result = default(Nullable<T>);
        return false;
    }
    else {
        T t;
        bool success = Parse(s, out t);
        Nullable<T> n = new Nullable<T>();
        n.Value = t;
        result = n;
        return success;
    }
}

 

At this point it's just a matter of adding public methods for each type that you need to support. I've added two methods, one for DateTime and one for Int32. Here is the complete Nullable struct:

public struct Nullable<T> {
 
    private static bool _isnull;
    private T _value;
    private delegate bool TryParseDelegate<T>(string s, out T result);
 
    static Nullable() {
        _isnull = true;
    }
        
    public bool IsNull {
        get { return _isnull; } 
    }
 
    public T Value {
        get {
            return _value;
        }
        set {
            _value = value;
            _isnull = false;
        }
    }
 
    public static bool TryParse(string s, out Nullable<Int32> result) {
        return ParseNullable<Int32>(s, out result, Int32.TryParse);
    }
 
    public static bool TryParse(string s, out Nullable<DateTime> result) {
        return ParseNullable<DateTime>(s, out result, DateTime.TryParse);
    }
 
    private static bool ParseNullable<T>(string s, out Nullable<T> result, TryParseDelegate<T> Parse) where T : struct {
        if (string.IsNullOrEmpty(s)) {
            result = default(Nullable<T>);
            return false;
        }
        else {
            T t;
            bool success = Parse(s, out t);
            Nullable<T> n = new Nullable<T>();
            n.Value = t;
            result = n;
            return success;
        }
    }
}

 

To exercise the struct:

class Program {
    static void Main(string[] args) {
            
        // exercise Nullable<T> with type DateTime
        Nullable<DateTime> ndt = new Nullable<DateTime>();
        Console.WriteLine(string.Format("Is null? {0}", ndt.IsNull.ToString()));
        Console.WriteLine(string.Format("Value is {0}", ndt.Value.ToString()));
        ndt.Value = DateTime.Now;
        Console.WriteLine(string.Format("Is null? {0}", ndt.IsNull.ToString()));
        Console.WriteLine(string.Format("Value is {0}", ndt.Value.ToString()));
        Console.WriteLine();
            
        // exercise Nullable<T> with type Int32
        Nullable<int> nint = new Nullable<int>();
        Console.WriteLine(string.Format("Is null? {0}", nint.IsNull.ToString()));
        Console.WriteLine(string.Format("Value is {0}", nint.Value.ToString()));
        nint.Value = 42;
        Console.WriteLine(string.Format("Is null? {0}", nint.IsNull.ToString()));
        Console.WriteLine(string.Format("Value is {0}", nint.Value.ToString()));
            
        // exercise TryParse functionality
        if (Nullable<DateTime>.TryParse(DateTime.Now.ToString(), out ndt)) {
            Console.WriteLine(string.Format("Successful TryParse with result {0}", ndt.Value.ToString()));
        }
 
        if (Nullable<int>.TryParse("99", out nint)) {
            Console.WriteLine(string.Format("Successful TryParse with result {0}", nint.Value.ToString()));
        }
        Console.ReadKey();         
    }
}

 

Looking for custom .NET solutions or help with an existing project? Give me a call at (503) 475-3808 and let's talk about your situation.

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5


About

SquareWidget LLC is an Oregon-based software development company that specializes in handcrafted .NET software solutions.

Search

Archive

Categories


Sign in