tisdag 4 augusti 2009

Sorting in nHibernate using an aggregate function

I was recently porting the data layer in an application from LINQ to SQL to nHibernate.
While doing this I found some parts that turned out to be somewhat more tricky in nHib then in plain LINQ.

First, lets imaging we have the following bi-directional objects:

class Item
{
public int ID {get;set;}
public int Type {get;set;}
public List Reviews {get;set;}
}

class ItemReview
{
public int ID {get;set;}
public Item Item {get;set;}
public int Grade {get;set;}
}

class ItemProjection
{
public Item Item {get;set;}
public int GradeCount {get;set;}
}

Consider the following LINQ query:
var items =
db.Items
.Where(i=>i.Type==5)
.Select(i=> {
return new ItemProjection()
{
Item = i,
Grade = i.ItemReviews.Sum(x=>x.Grade)
};
})
.OrderByDescending(x=>x.Grade).Take(5).ToList();

Basically what we are doing is that we select the 5 items which has the highest summary of review grades and project those into a new object. LINQ to SQL does a fantastic job to translate this into one single SQL query that works perfectly.

Now over to nHibernate. First I thought that "LINQ to nHibernate" would be smart enough to translate the query into a valid SQL statement. Well, as you can imagine, it did not work.

So, I turned to Google and the nHibernate documentation and started to look a "Projection" and "aliases". By using "Projections" you can create aggregate functions in the query that can be used as an order statement.

This is what I came up with:
var criteria = session.CreateCriteria("itemReview")
.CreateAlias("itemReview.Item", "item")
.SetProjection(
Projections.ProjectionList()
.Add(Projections.Sum("Grade"), "gradeSum")
.Add(Projections.GroupProperty("Item"))
)
.AddOrder(Order.Desc("gradeSum"))
.Add(NHibernate.Criterion.Expression.Eq("item.TypeID", typeID));
criteria.SetMaxResults(top);
var list = criteria.List();
return list.Select(o =>return new ItemProjection() {Item=(Item)o[1], Grade=(int)o[0]).ToList();


We start to create a "Criteria" for the "ItemReview" and then we add a few aliases followed by a "ProjectionList" that includes one "Sum" and on "GroupProperty" projection. The Summary is then used as an Order criteria. At last we "List<>" the criteria using the "object[]" type and projects the list into the "ItemProjection" type (I know, I know, the uggly cast between the object array and the properties isn't very nice...).

tisdag 19 maj 2009

Extended OrderBy

Extension methods for OrderBy and ThenBy methods, introducing two new features:
* The possibility to determine sort direction ascending/descending by passing a boolean value, rather than using a whole different method.
* The possibility to pass a string value for the property to sort by, which will be resolved using reflection

Both features may come in handy when sorting depends upon user input, such as the ability to resort a table by clicking table headers.

Courtesy of David Hedlund.

public static IOrderedEnumerable<TResult> OrderBy<TResult>(this IEnumerable<TResult> source, string propertyName)
{
   return OrderBy(source, propertyName, true);
}

public static IOrderedEnumerable<TResult> OrderByDescending<TResult>(this IEnumerable<TResult> source, string propertyName)
{
   return OrderBy(source, propertyName, false);
}

public static IOrderedEnumerable<TResult> OrderBy<TResult>(this IEnumerable<TResult> source, string propertyName, bool ascending)
{
   System.Reflection.PropertyInfo prop = typeof(TResult).GetProperty(propertyName);
   Func<TResult, object> orderBy = (i => prop.GetValue(i, null));
   return source.OrderBy(orderBy, ascending);
}

public static IOrderedEnumerable<TResult> OrderBy<TResult,TKey>(this IEnumerable<TResult> source, Func<TResult, TKey> keySelector, bool ascending)
{
   Func<Func<TResult, TKey>, IOrderedEnumerable<TResult>> orderMethod = source.OrderBy;

   if(!ascending)
      orderMethod = source.OrderByDescending;

   return orderMethod.Invoke(keySelector);
}

public static IOrderedEnumerable<TResult> ThenBy<TResult>(this IOrderedEnumerable<TResult> source, string propertyName)
{
   return ThenBy(source, propertyName, true);
}

public static IOrderedEnumerable<TResult> ThenByDescending<TResult>(this IOrderedEnumerable<TResult> source, string propertyName)
{
   return ThenBy(source, propertyName, false);
}

public static IOrderedEnumerable<TResult> ThenBy<TResult>(this IOrderedEnumerable<TResult> source, string propertyName, bool ascending)
{
   System.Reflection.PropertyInfo prop = typeof(TResult).GetProperty(propertyName);
   Func<TResult, object> orderBy = (i => prop.GetValue(i, null));
   return source.ThenBy(orderBy, ascending);
}

public static IOrderedEnumerable<TResult> ThenBy<TResult,TKey>(this IOrderedEnumerable<TResult> source, Func<TResult, TKey> keySelector, bool ascending)
{
   Func<Func<TResult, TKey>, IOrderedEnumerable<TResult>> orderMethod = source.ThenBy;

   if(!ascending)
      orderMethod = source.ThenByDescending;

   return orderMethod.Invoke(keySelector);
}

tisdag 31 mars 2009

Removing a subdirectory from your application will cost you your session state

Encountered a curious error, with users being logged out from an admin application, when they removed a folder. Turns out the behavior is by design: Removing a folder anywhere in your application, programatically or manually, will cause a full AppDomain recycle, in just the same manner as uploading a DLL file to bin or making a change to web.config does, and that's all there's to it.

Managed to come up with only one work-around, that'll still allow the file change notifier to monitor web.config and bin directory, and it's not very pretty, but it seems to work:
private void SafelyDeleteFolder(string folder, bool recursive)
{
   //FIX disable AppDomain restart when deleting subdirectory
   //This code will turn off monitoring from the root website directory.
   //Monitoring of Bin, App_Themes and other folders will still be operational, so updated DLLs will still auto deploy.

   System.Reflection.PropertyInfo p = typeof(HttpRuntime).GetProperty("FileChangesMonitor", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Static);

   object o = p.GetValue(null, null);
   System.Reflection.FieldInfo f = o.GetType().GetField("_dirMonSubdirs", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.IgnoreCase);

   object monitor = f.GetValue(o);
   System.Reflection.MethodInfo m = monitor.GetType().GetMethod("StopMonitoring", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic);
   m.Invoke(monitor, new object[] { });

   System.IO.Directory.Delete(folder, recursive);
}

tisdag 24 mars 2009

Default AJAX Object for calling WebMethods from jQuery

Calling WebMethods from jQuery is a neat way of dealing with AJAX requests, both on the client- and serverside, but the AJAX object expected by jQuery requires a lot of parameters to be set, in order to work properly with .NET WebMethods. Properties that usually remain constant, yet always have to be previded (i.e. a lot of unnecessary duplication of code). To resolve the issue, I have written the following snippet:
Object.prototype.toAjaxObject = function()
{
   if(!this.type) this.type = 'POST';
   if(!this.contentType) this.contentType = 'application/json; charset=utf-8';
   if(!this.dataType) this.dataType = 'json';

   if(!this.url && typeof this.method == 'string')
   {
      this.url = location.href.substring(0, location.href.indexOf('?')) + '/' + this.method.toString();
      this.method = undefined;
   }

   if(typeof this.data != 'string' && typeof JSON =='object')
      if(typeof JSON.stringify == 'function')
         this.data = JSON.stringify(this.data);

   return this;
}
The above code adds an extension method to all JavaScript objects, which populates some properties with default values provided that these values have not already been set. Hence, you can still always override the use of any default value, at will.

As added value, there is a check for whether the data parameter is an object, and the function JSON.stringify is defined. If they are, data will be stringified. That is, if you know you're working in a project where JSON serialization functions will always be at hand, you can pass the data object as a custom JSON object, rather than a valid JSON-parseable string.

Another feature is that the url parameter is not required. Instead, it is possible to pass a method parameter, which will set url to a WebMethod call to that method, assuming the current url is used, and then remove the method parameter from the object. For this to work, it is required that url is not set.

Including the above snippet, it is therefore possible to reduce this piece of code code:
$.ajax({
   type: 'POST',
   url: 'MyPage.aspx/GetUser',
   contentType: 'application/json; charset=utf-8',
   dataType: 'json',
   data: '{"id":'+id+'"}',
   success: myCallback
});
To the following:
$.ajax({
   method: 'GetUser',
   data: {id:id},
   success: myCallback
}.toAjaxObject());
Again, provided that a serializer function called JSON.stringify is present. Otherwise, the data property must still be passed as a string in the manner of the first example.

Update


Looks like jQuery wasn't quite ready for object extension like this. The code works fine, but what Object.prototype does is to extend all Objects with some new functionality. Ran in to trouble as dialog wasn't prepared that the object passed might be extended with a function like this one. Eventually retorted to what feels pretty much like a hack. I would rather see that the jQuery UI team fixed up their components to allow the use of regular JavaScript like this, but for now, the following will do the trick:
$.ajaxCore = $.ajax;
$.ajax = function(opts)
{
   var addDefaults = arguments[0] ? arguments[0] : false;
   if(addDefaults)
   {
      if(!opts.type) opts.type = 'POST';
      if(!opts.contentType) opts.contentType = 'application/json; charset=utf-8';
      if(!opts.dataType) opts.dataType = 'json';

      if(!opts.url && typeof opts.method == 'string')
      {
         opts.url = location.href.substring(0, location.href.indexOf('?')) + '/' + opts.method.toString();
         opts.method = undefined;
      }

      if(typeof opts.data != 'string' && typeof JSON =='object')
         if(typeof JSON.stringify == 'function')
            opts.data = JSON.stringify(opts.data);
   }

   $.ajaxCore(opts);
}
The method call should, then, be modified accordingly:
$.ajax({
   method: 'GetUser',
   data: {id:id},
   success: myCallback
}, true);

fredag 27 februari 2009

Potentially dangerous Request in MVC

While developing an admin-page in MVC I kept getting the old "Potentially dangerous Request..." which occurs when trying to submit form data including html tags.

After adding the "validateRequest=false" to the pages-element in web.config (which turns this feature off in asp.net webforms) and checking that the page directive didn't ovveride this setting I was getting quite annoyed that I still got the exception on my page.

However - in MVC you need to set an attribute on your action in the controller to turn off validation:

[ValidateInput(false)]
public ActionResult Edit(int id, FormCollection collection)
{}


In my opionon I think that the setting is web.config should be read by the MVC actions as well as you otherwise need to define this attribute on all actions that might include html as a string parameter.

tisdag 17 februari 2009

JavaScript context binding

One of the neatest features in the JavaScript library Prototype is its helper function for context binding. This is one feature to which I have yet to find the correspondence in jQuery. The following code is the functional equivalent of the Prototype solution, which I've found use for too many times:

<script type="text/javascript">
Function.prototype.bind = function(context)
{
   var __method = this;
   var __args = new Array();

   for(it = 1; it < arguments.length; it++)
      __args.push(arguments[it]);

   return function() {
      for(ait = 0; ait < arguments.length; ait++)
         __args.push(arguments[ait]);

      __method.apply(context, __args)
   };
}
</script>
So what does it do? Well, it makes sure that all functions will have a function of their own, called bind. Bind receives a context, and makes sure that the function is always invoked from that context - i.e. that the "this." notation in the function will always reference the given context.

This solves a common JavaScript problem, when working with callback functions. Consider the following:

function User(username, password)
{
   this.username = username;
   this.password = password;
   this.tryLogin = function()
   {
      $.ajax({
         type: "POST",
         url: "Default.aspx/Login",
         ...
         success: function(msg)
         {
            if(msg == 'true')
               alert('Welcome '+this.username);
         }
   }
}
In the above code, the function passed along as a callback will be called from the jQuery AJAX handler, hence "this.username" will look for a username property in $.ajax() (and presumably fail to find one). Now, if we had included the snippet at the top of this entry, we would've been able to modify the callback declaration to the following:

success: function(msg)
{
   alert('Welcome '+this.username);
}.bind(this);
Here, instead of passing in assigning the success property with function x, we're passing x to bind, and assigning the value that bind returns, which is x hooked up to a specified context (this, as passed to bind).

For added value, bind also comes with the option to specify additional arguments to be passed along to the function, as it is being invoked. To demonstrate why this is useful, consider the following scenario:

<button id="btn0">Button0</button>
<button id="btn1">Button1</button>
<button id="btn2">Button2</button>
<script type="text/javascript">
function Person(name) {
   this.name = name;
   this.logClick = function(x) {
      alert(this.name + ' clicked button ' + x);
   };
   this.start = function() {
      for(var i = 0; i < 3; i++)
      {
         $('#btn'+i).onclick = function() { this.logClick(i) }.bind(this);
      }
   };
}

var p = new Person('Test');
p.start();
</script>
Now, the start function will hook up the three buttons with onclick events that will trigger the highlighted function. The bind is there, so we will be able to access this.name in logClick without a problem, but our desired effect here - to get Button1, say, to alert "Test clicked button 1" - will still not be met: the i parameter won't be accessed until the actual click event occurs, and by then, the for loop will have completed, and i will have the value of "3". All buttons will produce the same output. What we want, here, is to tell the function to use the value that the variable contained at the time of the event wireup. Again, bind comes to the rescue:

$('#btn'+i).onclick = function(x) { this.logClick(x) }.bind(this, i);
The above modification will solve the issue, and produce the desired output. Now that we're binding the parameter in bind, we can, of course, get rid of the delegate entirely:

$('#btn'+i).onclick = this.logClick.bind(this, i);

tisdag 30 december 2008

Making the JsonResult in MVC ignore a property

In MVC each controller action returns an "ActionResult". This can be a View, Content (plain text) or JsonResult for example. With JsonResult the output will (obviously) be serialized to JSON.

However - when using any serializeri on classes with bi-directional fields/properties you will run into problems as the serialization will lead to a endless loop trying to follow the bi-directional fields back and forth.

Using other serializer such as an XML serializer for example you just tag the bi-directional property with an "XMLIgnore" attribute and the serializer will basically ignore the field during serialization.

But trying to find a way for the JsonResult was not very easy as there is basically no subjects on this to be found using Google (at this time). However, using Reflector I found that the JsonResult is using the serializer "JavaScriptSerializer" found in "System.Web.Scrtipt.Serializer" (System.Web.Extensions.dll version 3.5). Following the code I found that the attribute to use to ignore a field is "ScriptIgnore" also found in "System.Web.Script.Serialization".

So, problem solved: Use the "ScriptIgnore" attribute to make the Json serializer in MVC ignore your bi-directional fields!