Thursday, September 23, 2010

Things good to know about SQL State Server

While installing a SQL State Server last week, I came across a few things worth sharing about the installation and use of SQL State Server.

Finding a good tutorial

There are lots of tutorials out there on how to install SQL State Server but most of them are not great. To do a basic installation you only need this Msdn documentation on how to run the Aspnet_regsql.exe tool and edit your web.config.

All the objects in Session need to be serializable

If you try to store an object in Session which isn't marked as serializible an HttpException will get thrown with following message.
Unable to serialize the session state. In 'StateServer' and 'SQLServer' mode, ASP.NET will serialize the session state objects, and as a result non-serializable objects or MarshalByRef objects are not permitted. The same restriction applies if similar serialization is done by the custom session state store in 'Custom' mode.
Marking your classes with the Serializible attribute shouldn't be a problem. Be careful when storing WebControls in Session though, most of these aren't marked as serializible!

When you are using UpdatePanels this exception doesn't fully propagate to the front-end. The javascript error shown will have following message.
Error: Sys.WebForms.PageRequestManagerServerErrorException: An unknown error occurred while processing the request on the server. The status code returned from the server was: 500
Use the same machinekey on multiple servers

To make the SQL State Server work across servers hosting the same application, you need to make sure the machineKey element in the machine.config is identical.

Redundancy options are limited

Looking at the Msdn documentation, it looks like only SQL Server clustering is supported. You can't specify a Failover Partner, so SQL Server mirroring isn't supported.
In SQLServer mode, you can configure several computers running SQL Server to work as a failover cluster, which is two or more identical computers running SQL Server that store data for a single database. If one computer running SQL Server fails, another server in the cluster can take over and serve requests without session-data loss.
More experience?

Do you know more things good to know about SQL State Server?

Saturday, September 18, 2010

Building a tagcloud with jQuery and ASMX Webservices

Generating tagclouds is nothing new. People have been generating tagclouds server-side since the seventies. Lately more and more tagclouds are being generated client-side.

There is nothing wrong with generating tagclouds server-side. Telerik has a great tagcloud server control. Generating tagclouds server-side can bring some overhead though, so depending on the scenario and the requirements you might decide to do it client-side. There are a ton of fancy ready-to-use jQuery tagcloud plug-ins out there. None of them met my requirements perfectly, so I decided to do it myself.

In this post you can find my own implementation. It's simple, straightforward and a decent base to start experimenting yourself.

Making the tags available through an ASMX Webservice

In my implementation I use an ASMX Webservice to expose a TagCollection. The TagCollection is an object which contains a List<Tag>. The TagCollection also has a MaxWeight property which we will need on the client-side to calculate the relative weight.

   1:  public class TagCollection {
   2:      public TagCollection() { }
   3:   
   4:      public TagCollection(List<Tag> items, int maxWeight) {
   5:          this.Items = items;
   6:          this.MaxWeight = maxWeight;
   7:      }
   8:   
   9:      public List<Tag> Items { get; set; }
  10:      public int MaxWeight { get; set; }
  11:  }


A Tag is a simple object with two properties: Value and Weight.

   1:  public class Tag {
   2:      public Tag() { }
   3:   
   4:      public Tag(string value, int weight) {
   5:          this.Value = value;
   6:          this.Weight = weight;
   7:      }
   8:   
   9:      public string Value { get; set; }
  10:      public int Weight { get; set; }
  11:  }


In the ASMX WebService I created the GetTagCollection method which returns a TagCollection. Don't forget to uncomment the [System.Web.Script.Services.ScriptService] declaration and to add the [ScriptMethod(ResponseFormat = ResponseFormat.Json)] declaration to the GetTagCollection method.

   1:  [WebService(Namespace = "http://TagCloudWithJquery.com")]
   2:  [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
   3:  [System.Web.Script.Services.ScriptService]
   4:  public class TagService : System.Web.Services.WebService { 
   5:      [WebMethod]
   6:      [ScriptMethod(ResponseFormat = ResponseFormat.Json)]
   7:      public TagCollection GetTagCollection() {
   8:          var items = new List<Tag>() {
   9:                  new Tag(".NET", 20),
  10:                  new Tag("CodeSnippets", 15),
  11:                  new Tag("...", 10),
  12:                  new Tag("ASP.NET", 18)                
  13:              };
  14:          var maximumWeight = items.Max(i => i.Weight);
  15:   
  16:          var tagCollection = new TagCollection(items, maximumWeight);        
  17:   
  18:          return tagCollection;
  19:      }
  20:  }


Consuming the ASMX Webservice

Consuming the ASMX Webservice with jQuery is relatively simple. If you are new to consuming ASMX Webservices with jQuery, I advice you to read this excellent article.

When the document is ready I make a call to the ASMX Webservice and when the call is successful all the work gets passed to the onTagCloudSuccess function.

   1:  $(document).ready(setupTagCloud);
   2:   
   3:  function setupTagCloud() {
   4:      $.ajax({
   5:          type: "POST",
   6:          contentType: "application/json; charset=utf-8",
   7:          url: "Services/TagService.asmx/GetTagCollection",
   8:          data: "{}",
   9:          dataType: "json",
  10:          success: onTagCloudSuccess
  11:      });
  12:  }


Don't forget to add a script reference to the latest version of jQuery.

Generating the tagcloud

All the work happens in the onTagCloudSuccess function. In this function I iterate over all the items in the TagCollection. For each item I calculate its relative weight using the TagCollections MaxWeight property. Depending on the relative weight the tag gets a different css class. This logic can be found in the getCloudItemClass function.

Finally I append a new listitem to the unordered list 'items' using the tags value and the calculated css class.

   1:  function onTagCloudSuccess(data, textStatus) {      
   2:      var maxWeight = data.d.MaxWeight;
   3:   
   4:      $.each(data.d.Items, function(i, item) {
   5:          var itemWeight = item.Weight;
   6:          var relativeItemWeight = itemWeight / maxWeight;
   7:          var itemClass = getCloudItemClass(relativeItemWeight);            
   8:   
   9:          $("#items").append("<li class='" + itemClass + "'>" + item.Value + "</li>");
  10:      });       
  11:  }
  12:   
  13:  function getCloudItemClass(weight) {
  14:      if (weight < 0.35) {
  15:          return "light";
  16:      } else if (weight < 0.7) {
  17:          return "medium";
  18:      } else {
  19:          return "heavy";
  20:      }       
  21:  }


Don't forget to add an unordered list with the id set to 'items' to your page.

So far this looks something like this.



I used following css to make the unordered list look like a tagcloud.

   1:  #items li {               
   2:      display: inline-block;         
   3:      margin: 4px;
   4:  }
   5:   
   6:  .heavy { font-size: 60px ; color:Red; }
   7:  .medium { font-size: 30px; color:Orange; }
   8:  .light { font-size: 14px ; color:Yellow; }


Setting the display to inline-block on the listitems makes the unordered list go horizontal instead of vertical. The heavy, medium and light classes are used to give the tagclouditems a style that matches with their heaviness.

The final result looks like this.



Different ways of marking up a tagcloud can be found in this article.

Get the source

You can find the full source here.

Feedback

As always I want to hear your feedback. Please tell me how this implementation can be improved.