Monthly Archive for August, 2008

Aug08 Minneapolis Silverlight User Group

Tips and Tricks for Dealing with Data in Silverlight

Scott Davis

Ignition Point Solutions

Scott talked about why you may want to use Silverlight for line-of-business applications. He asks: Is anyone aware of any good reporting solutions written for Silverlight?

When planning your Silverlight application, a design consideration is that everything is done asyncronously. Scott has developed a technique which keeps his data updated using threaded services.

WCF full duplex communications do not work in Silverlight.

In the demonstrated WCF communication, the Silverlight client sends a request to the server that says, send me updates since the last time I came back. This is done independent of the UI and thus the UI stays updated.

Aug08 Silverlight User Group

Using System.Web.Routing with Castle MonoRail

Several folks have already posted about using System.Web.Routing with standard ASP.NET WebForms, and I’d like to extend the concept for use with MonoRail. The default MonoRail router is not terribly sophisticated. It works by using pairs of regular expressions to match and then rewrite a url into /area/controller/action/id format. Here is an example from the documentation:

<rule>
	<pattern>(/blog/posts/)(\d+)/(\d+)/(.)*$</pattern>
	<replace><![CDATA[ /blog/view.rails?year=$2&month=$3 ]]></replace>
</rule>

It’s functional, and you can achieve most any route that you’d like, but the syntax is awkward. System.Web.Routing provides a much cleaner solution:

routes.Map(
   "MonthlyArchives",
   "/blog/posts/{year}/{month}",
   new { controller = "blog", action = "view" }
);

ScottGu gives a good tutorial.

Before we can get started, we’ll need to examine how both MonoRail and System.Web.Routing perform routing and IHttpHandler creation. Unfortunatley, an explanation of ASP.NET handlers and modules is beyond the scope of this post. Although dated, there are some good overviews.

System.Web.Routing runs as an IHttpModule, examines incoming requests, matches them against registered routes, and, if a match is found, uses the IRouteHandler associated with the matched route to create an IHttpHandler, which will process the request.

MonoRail on the other hand has a straightforward IHttpHandlerFactory implementation, MonoRailHttpHandlerFactory, which parses the request url for /area/controller/action.extension/id syntax. If the MonoRail routing module is installed, that module will rewrite the url and create a RouteMatch object in the current HttpContext.Items collection.

Therefore a successful integration will need to use the existing MonoRailHttpHandlerFactory infrastructure–directly creating controllers requires far too much supporting context which the factory provides–yet allow System.Web.Routing to inform the controller/action selection using its much richer RouteData output.

We’ll start by subclassing the MonoRailHttpHandlerFactory and adding the IRouteHandler interface:

public class MonoRailRouteHandler : MonoRailHttpHandlerFactory, IRouteHandler
{
    #region IRouteHandler Members

    public IHttpHandler GetHttpHandler(RequestContext requestContext)
    {
        var context = HttpContext.Current;
        return GetHandler(
            context,
            context.Request.RequestType,
            context.Request.RawUrl,
            context.Request.PhysicalApplicationPath);
    }

    #endregion
}

This handler is clearly just a passthrough to the underlying MonoRail GetHandler factory method. We’ll need to somehow use the routing information from System.Web.Routing to tell GetHandler what to create. Fortunately, Castle is very flexible here, and has a dependency, IUrlTokenizer, which takes a url and returns a UrlInfo structure. UrlInfo contains area, controller, and action properties (amongst other less interesting properties). Next lets build a IUrlTokenizer that just provides whichever UrlInfo it’s told to:

public class MockUrlTokenizer : IUrlTokenizer
{
    [ThreadStatic]
    private static UrlInfo _currentUrlInfo;

    public static UrlInfo CurrentUrlInfo
    {
        get { return _currentUrlInfo;}
        set { _currentUrlInfo = value; }
    }

    public void AddDefaultRule(string url, string area, string controller, string action)
    {
        throw new NotImplementedException();
    }

    public UrlInfo TokenizeUrl(string filePath, string pathInfo, Uri uri, bool isLocal, string appVirtualDir)
    {
        return CurrentUrlInfo;
    }
}

Finally we’ll modify our IRouteHandler to use the new UrlTokenizer as well as convert the System.Web.Routing RouteData to a MonoRail RouteMatch:

public class MonoRailRouteHandler : MonoRailHttpHandlerFactory, IRouteHandler
{
    public MonoRailRouteHandler()
    {
        UrlTokenizer = new MockUrlTokenizer();
    }

    public IHttpHandler GetHttpHandler(RequestContext requestContext)
    {
        var context = HttpContext.Current;

        // parse urlinfo from the s.w.routing data
        var values = requestContext.RouteData.Values;
        MockUrlTokenizer.CurrentUrlInfo = new UrlInfo(
            context.Request.Url.Host,
            context.Request.Url.Host,
            context.Request.ApplicationPath,
            context.Request.Url.Scheme,
            context.Request.Url.Port,
            context.Request.RawUrl,
            values.ContainsKey("area") ? (string)values["area"] : String.Empty,
            values.ContainsKey("controller") ? (string)values["controller"] : String.Empty,
            values.ContainsKey("action") ? (string)values["action"] : String.Empty,
            String.Empty,
            context.Request.Url.Query
        );

        // create a monorail routematch
        var match = new RouteMatch();
        foreach (var pair in values)
        {
            match.Parameters.Add(pair.Key, pair.Value as string);
        }
        context.Items[RouteMatch.RouteMatchKey] = match;

        // allow standard monorail handler to proceed
        return GetHandler(
            context,
            context.Request.RequestType,
            context.Request.RawUrl,
            context.Request.PhysicalApplicationPath);
    }
}

Now you can simply register routes normally, using the new MonoRailRouteHandler.

Go ahead and grab the source file which includes a couple of extras:
MonoRailRouteHandler.cs.




Creative Commons Attribution 3.0 United States
Creative Commons Attribution 3.0 United States