NHibernate To DataSet
Ayende just posted a very simple solution to converting the result of an NHibernate Query / Criteria to a DataSet. Here is something that I have used in the past.
public DataTable CriteriaToDataTable<CriteriaClass>(ICriteria query, String dataTableName, params String[] properties) { DataTable dt = new DataTable(dataTableName); int i; foreach (String prop in properties) dt.Columns.Add(prop.Replace('.', '_'), ReflectionUtil.GetPropertyType(typeof (CriteriaClass), prop)); foreach (Object obj in query.List()) { i = 0; Object[] vals = new Object[properties.Length]; foreach (String prop in properties) { vals[i] = ReflectionUtil.GetPropertyValue(obj, prop); i++; } dt.Rows.Add(vals); } return dt; }
And the cheap reflection utility
public class ReflectionUtil { ReflectionUtil() { } public static bool InstanceOf<T>(Object instance) { return typeof (T).IsInstanceOfType(instance); } #region Assembly Loading /// <summary> /// Checks if the assembly is loaded in the current AppDomain /// </summary> /// <param name="assemblyName">The full assembly name to search for.</param> /// <returns>true if the assembly is loaded otherwise false</returns> public static bool AssemblyLoaded(String assemblyName) { foreach (Assembly ass in AppDomain.CurrentDomain.GetAssemblies()) { if (ass.GetName().Name.Equals(assemblyName)) return true; } return false; } /// <summary> /// Loads the assembly into the current AppDomain /// </summary> /// <param name="assemblyName">The full assembly name to load into the current AppDomain</param> /// <returns>The loaded assembly</returns> public static Assembly LoadAssembly(String assemblyName) { return Assembly.Load(assemblyName); } #endregion #region Activate Classes /// <summary> /// Attempts to create an instance by calling the default constructor of the class. /// </summary> /// <param name="className">The full class name of the class to activate.</param> /// <param name="assemblyName">The full assembly name containing the class to activate.</param> /// <returns>The activated class</returns> /// <remarks>The class name must be the full name eg. MyNamespace.ClassName</remarks> public static Object CreateLocalInstance(String className, String assemblyName) { Object obj = null; try { Assembly ass = LoadAssembly(assemblyName); obj = Activator.CreateInstance(ass.GetType(className, true, true)); } catch { } return obj; } /// <summary> /// Attempts to create an instance by calling the default constructor of the class. /// </summary> /// <typeparam name="T">The Type to return.</typeparam> /// <param name="className">The full class name of the class to activate.</param> /// <param name="assemblyName">The full assembly name containing the class to activate.</param> /// <returns>The activate class casted to type T</returns> /// <remarks>The class name must be the full name eg. MyNamespace.ClassName</remarks> public static T CreateLocalInstance<T>(String className, String assemblyName) { return (T) CreateLocalInstance(className, assemblyName); } public static T CreateLocalInstance<T>(Type type) { return (T) Activator.CreateInstance(type); } /// <summary> /// Attempts to create an instance by calling the default constructor of the class. /// </summary> /// <typeparam name="T">The Type to return.</typeparam> /// <param name="className">The class to activate.</param> /// <returns>The activate class casted to type T</returns> /// <remarks>The class name must be the full name eg. MyNamespace.ClassName</remarks> public static T CreateLocalInstance<T>(String className) { return (T) Activator.CreateInstance(Type.GetType(className)); } #endregion #region Type Getters /// <summary> /// Returns the Type represented by the assemblyName and className /// </summary> /// <param name="assemblyName">The full assembly name to check for the Type</param> /// <param name="className">The full class name of the Type to get.</param> /// <returns>The Type if found otherwise null.</returns> /// <remarks>The class name must be the full name eg. MyNamespace.ClassName</remarks> public static Type GetType(String assemblyName, String className) { Type t = null; try { t = Type.GetType(className, true, true); } catch { try { Assembly ass = LoadAssembly(assemblyName); t = ass.GetType(className, true, true); } catch { t = null; } } return t; } #endregion public static Object GetPropertyValue(Object rootInstance, String propertyPath) { if (rootInstance == null) return null; Type t = rootInstance.GetType(); if (String.IsNullOrEmpty(propertyPath)) return null; int dotIdx = propertyPath.IndexOf('.'); if (dotIdx <= 0) { return SafeGetProperty(t, propertyPath).GetValue(rootInstance, null); } String prop = propertyPath.Substring(0, dotIdx); return GetPropertyValue(SafeGetProperty(t, prop).GetValue(rootInstance, null), propertyPath.Substring(dotIdx + 1)); } private static PropertyInfo SafeGetProperty(Type t, string property) { try { return t.GetProperty(property); } catch (AmbiguousMatchException) { return t.GetProperty(property, BindingFlags.Public | BindingFlags.DeclaredOnly | BindingFlags.Instance); } return null; } public static T GetPropertyValue<T>(Object rootInstance, String propertyPath) { if (rootInstance == null) return default(T); Type t = rootInstance.GetType(); if (String.IsNullOrEmpty(propertyPath)) return default(T); int dotIdx = propertyPath.IndexOf('.'); if (dotIdx <= 0) return (T) SafeGetProperty(t, propertyPath).GetValue(rootInstance, null); String prop = propertyPath.Substring(0, dotIdx); return GetPropertyValue<T>(SafeGetProperty(t, prop).GetValue(rootInstance, null), propertyPath.Substring(dotIdx + 1)); } public static Type GetPropertyType(Type rootType, String propertyPath) { PropertyInfo pi; if (rootType == null) throw new ArgumentNullException("rootType", "root type is null for property path " + propertyPath); if (String.IsNullOrEmpty(propertyPath)) throw new ArgumentException("propertyPath", "propertyPath cannot be null or empty for type " + rootType.FullName); int dotIdx = propertyPath.IndexOf('.'); if (dotIdx <= 0) { pi = rootType.GetProperty(propertyPath); if (pi != null) return pi.PropertyType; foreach (Type t in rootType.GetInterfaces()) { pi = SafeGetProperty(t, propertyPath); if (pi != null) return pi.PropertyType; } return typeof (Object); } String prop = propertyPath.Substring(0, dotIdx); pi = SafeGetProperty(rootType, prop); if (pi != null) return GetPropertyType(pi.PropertyType, propertyPath.Substring(dotIdx + 1)); foreach (Type t in rootType.GetInterfaces()) { pi = SafeGetProperty(t, prop); if (pi != null) break; } if (pi == null) return typeof (Object); return GetPropertyType(pi.PropertyType, propertyPath.Substring(dotIdx + 1)); } }
Cheers, looks good to me and is something that should be a part of the NHib framework IMHO. I dont see DataSets as tying you down to a specific data access layer so I dont know why its not avalaiable in NHib other than keeping in sync with the Java port.
N/Hibernate is a totally different persistence model than ADO and DataSets. This something I haven’t used since the time I wrote the post. It was for some integration with the Telerik Rad WebControls for a legacy app.
There is a project in Castle Contrib called ARDataSource which may also help you.
http://svn.castleproject.org:8080/svn/castlecontrib/ardatasource/trunk/
This is great. However, there are a few improvements that could be made:
Support for Nullable Types in ReflectionUtil
Change CriteriaToDataTable() into a ToDataTable() extension method on NHibernate's IQuery/ICriteria interfaces.
Add support for aliasing column names (eg by passing in the column name like "XYZ as ABC".
It would be much more flexible if the "properties" string array was an IEnumerable<string>.
I'd love to let you have my private branch that adds these features. However, I can promise they'd only take you 15 minutes to implement.
Too late, cowboy