Wednesday 24 October 2012

MS Build Task to create jQuery validation metadata from DataAnnotation attributes in ASP.NET Web Api

DataAnnotations provide a great mechanism for simple model validation (string lengths, regex, ranges etc) upon submission to the server. However in today's solutions we usually want to evaluate the same rules on the client as the user is entering data - this adds an overhead of creating the same rules twice (once in js and once on the server).

One solution is to build the js rules directly from the DataAnnotations - the following snippet of code does this and demonstrates loading both built in DataAnnotations and custom annotations, it generates a module that can be loaded using requirejs to provide rule data for jQuery validation.

A set of rules is generated for every object in the namespace suplied in "ModelNamespace" that has DataAnnotations.

The following example is best used in builds build from the command line - the reason being that the assembly cannot be unloaded following inspection (I have not been able to come up with a solution for this short of building a full plugin mechanism for loading the assembly to be inspected).  My workaround for this is to only generate the js for release builds using the following task configuration:

  <Target Name="AfterBuild" Condition="$(Configuration) == 'Release'">
    <CreateJsFromAnnotations ModelAssembly="bin\WebInterface.dll" ModelNamespace="WebInterface.Models" OutputFile="Scripts\common\generated\ModelMetadata.js" />
  </Target>

Sample output would be something along the lines of:
define(
    [],
    function () {
        return {
  "folderValidation": {
    "folderapp": {
      "locationparsing": "PathOnly",
      "regex": "^[^\\s]*$",
      "maxlength": 256,
      "required": true
    },
    "folderproxy": {
      "locationparsing": "PathOnly",
      "regex": "^[^\\s]*$",
      "maxlength": 256,
      "required": true
    }
  }
};
    }
);

Wednesday 23 May 2012

Cannot find RIMIDEWin32Util.dll when running RIM BlackBerry JDE on x64


I recently had to install the RIM BlackBerry JDE 6.0.0 on my 64 bit dev PC and as often seems to be the case when I install a new Java based IDE I ran into some small configuration issues.

The Problem:
when I installed the BlackBerry JDE there were some warnings saying that this needs the 32 bit JDK, whereas the default for me is the 64 bit JDK.  I duly ignored the errors/warnings, I wasn't about to re-install my JDK because of this, besides, what's to say some other project won't depend on me having the 64 bit version.

The JDE ran OK and I played about a bit, don't get me started on that... and eventually closed it down.

Next time I fired it up I got:
Error - BlackBerry Java Development Environment.
Cannot find RIMIDEWin32Util.dll. This is a required component of the IDE.
Unsurprisingly my first port of call was Google, here came across various forum and StackOverflow solutions involving un-installing the JDK, rebooting - re-installing different versions etc etc... thinking cap on... that can't be right... time to try something else.

After a little refreshing my memory on CLASSPATH, JAVA_HOME etc I decided I was armed to fix this - so I went off to find the starting point for the JDE:
"C:\Program Files (x86)\Research In Motion\BlackBerry JDE 6.0.0\bin\ide.bat"

Wohoo! its a good old BAT file!

add the line
set PATH=C:\Program Files (x86)\Java\jre6\bin;%PATH%
to the top of it

Job Done

Reinstall my JDK?? Pah!

(admittedly it so happens that I had previously installed the 32 bit JDK and never uninstalled it).

Wednesday 21 March 2012

Using an IErrorHandler IServiceBehavior in WCF

It is sometimes useful to be able to customise the error handling behaviour of a WCF web service, I have found myself having to do this on a number of occasions, it is easy to do but it is also easy to run foul of little gotchas such as what is logged when, so I am posting a very basic solution here as a starting point for future reference.


Generally this is used to log all exceptions and to explicitly convert exceptions to faults, the simple solution here only uses the basic FaultException, but can be easily extended to use FaultException<T>.


WCF provides the IErrorHandler interface for our implementation of this functionality, here is what I have found to work (note the delegates/Actions are not necesarry - I just prefer not to have to modify the ErrorHandler for new projects):

public class ErrorHandler : IErrorHandler
{
    private readonly Action<exception> LogException;
    private readonly Action<message> LogFault;

    public ErrorHandler(Action<exception> logException, Action<message> logFault)
    {
        LogException = logException;
        LogFault = logFault;
    }

    public void ProvideFault(Exception error, MessageVersion version, ref Message fault)
    {
        if (error is FaultException) // Thrown by WCF - eg request deserialization problems, could also be explicitly thrown in code
        {
            LogFault(fault);
            return;
        }

        var faultCode = new FaultCode("UnknownFault");
        if (error is ArgumentOutOfRangeException)
        {
            faultCode = new FaultCode("ArgumentOutOfRange");
        }

        var action = OperationContext.Current.IncomingMessageHeaders.Action;
        fault = Message.CreateMessage(version, faultCode, error.Message, action);
        LogFault(fault);
    }

    public bool HandleError(Exception error)
    {
        // Logging of exceptions should occur here as all exceptions will hit HandleError, but some will not hit ProvideFault
        LogException(error);

        return false; // false allows other handlers to be called - if none return true the dispatcher aborts any session and aborts the InstanceContext if the InstanceContextMode is anything other than Single.
    }
}


Note that the error handler alone is not enough, we also need to create a  IServiceBehavior  that will add the  IErrorHandler :


public class ErrorHandlerBehavior : IServiceBehavior
{
    private readonly IErrorHandler handler;
    public ErrorHandlerBehavior(IErrorHandler errorHandler)
    {
        this.handler = errorHandler;
    }

    public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
    { }
    
    public void AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, Collection<serviceendpoint> endpoints, BindingParameterCollection bindingParameters)
    { }

    public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
    {
        foreach(ChannelDispatcher dispatcher in serviceHostBase.ChannelDispatchers)
        {
            dispatcher.ErrorHandlers.Add(handler);
        }
    }
}

The behaviour can then be added either through the web.config file or programatically to the System.ServiceModel.ServiceHost.Description.Behaviors.