Search This Blog

2012-03-31

Using Ninject with ASP.NET Web API

ASP.NET Web API is a reall cool feature that makes it easy to build HTTP services, and now it is a part of MVC 4. Ninject + Ninject.MVC is also a cool IoC tool but current version doesn't really work out the box within ApiController.
public class MyApiController : ApiController
{
  [Inject]
  public IRepository<Entity> repository { get; set; }

  public IQueryable<Entity> Get()
  {
    return repository.GetAll();
  }
}
It won't inject repository and you will get null reference exception. The reason is  that ApiController rely on another IDependencyResolver interface, from System.Web.Http.Services namespace. There is a question on stackoverflow and nice article explaining how it could be done.
Basically, the idea is fairly simple: just re-implement (i.e. copy-and-paste)  NinjectDependencyResolver - the System.Web.Mvc.IDependencyResolver implementation.
public class WebApiNinjectDependencyResolver : System.Web.Http.Services.IDependencyResolver
{
  private readonly IResolutionRoot resolutionRoot;

  public WebApiNinjectDependencyResolver(IResolutionRoot resolutionRoot)
  {
    this.resolutionRoot = resolutionRoot;
  }

  public object GetService(Type serviceType)
  {
    IRequest request = this.resolutionRoot.CreateRequest(serviceType, null, new Parameter[0], true, true);
    return this.resolutionRoot.Resolve(request).SingleOrDefault();
  }

  public IEnumerable<object> GetServices(Type serviceType)
  {
    return this.resolutionRoot.GetAll(serviceType, new IParameter[0]).ToList();
  }
}
All we need to do now is register it on Application Start.
public static class NinjectWebCommon
{
  private static readonly Bootstrapper bootstrapper = new Bootstrapper();

  public static void Start()
  {
    ...
    bootstrapper.Initialize(CreateKernel);
  }

  public static void Stop()
  {
    bootstrapper.ShutDown();
  }

  private static IKernel CreateKernel()
  {
    var kernel = new StandardKernel();

    ....

    RegisterServices(kernel);
    
    // Register implementation of System.Web.Http.Services.IDependencyResolver
    GlobalConfiguration.Configuration.ServiceResolver.SetResolver(new WebApiNinjectDependencyResolver(kernel));

    return kernel;
  }
  ...
}
That's all! Enjoy injections to your ApiControllers.

2012-03-26

My git ignore for ASP.NET

For those how use git Source Code Management for .NET/ASP.NET solutions there is a start-up routine to add irrelevant files and folders to ignore list. Just to make life easier I tried to create my own list based on common needs. 
First, we usually don't need bin and obj folder since we are interested in sources only; Visual Studio and Resharper files: *.suo, *.user*cache, _ReSharper*. If you use NuGet and NuGet Package Restore you are probably not interested in packages folder.
Another very controversial issue is to ignore upgrade and backup files after solution migration, but usually I don't need them as well :-): UpgradeLog.XML_UpgradeReport_FilesBackup.
Great TDD tools like NCrunch and NSubstitute could help you a lot, but  *.ncrunchproject*.ncrunchsolution, *NSubstitute* files might be ignored.
Finally, my .gitignore:
bin/
obj/
packages/
*.suo
*.user
*cache
_ReSharper*

*NSubstitute*

*.ncrunchproject
*.ncrunchsolution

UpgradeLog.XML
_UpgradeReport_Files
Backup
Obviously the list is not generic and could not be recommended for all projects, but at least it is useful for small and medium open-source solutions to keep sources clean and size-optimized.   

2012-03-18

Render external jQuery Template files with ASP.NET MVC 4

While developing more complex web applications using template engines (maybe together with Backbone.js, Knockout.js, etc.), we come to obvious idea to store template files separately.
Straightforward solution is to load a file with ajax request like:
$.get("templates/Contact.htm", function(template) {
  $("body").append(template);
});
Contact.htm:

There are several great posts about such an issue by Dave Ward, Ryan Niemeye. But I have a little bit modified solution...
New ASP.NET MVC 4 has delivered lots of content, and Bundling feature is a good option to work with scattered files.
Bundling represents a list of JavaScript or cascading style sheet (CSS) files that ASP.NET dynamically combines into a single virtual file that a browser can retrieve by using a single HTTP request.  It is a part of System.Web.Optimization namespace and can be downloaded as a Nuget package.
Bundling basically can combine any files and not js or css ones, so let's try to combine template "htm" files. To do that we simply need to register new Dynamic Folder Bundle on Application Start of Global.asax:
var templateBundle = new DynamicFolderBundle("htm", "*.htm", true);
var context = new BundleContext(new HttpContextWrapper(Context), new BundleCollection(), "~/Templates/htm");
templateBundle.EnumerateFiles(context);
BundleTable.Bundles.Add(templateBundle);
And that's it! With this code
BundleTable.Bundles.ResolveBundleUrl("~/Templates/htm)
we have a url to combined template file, so we can use it inside Razor view like:
$.get(@BundleTable.Bundles.ResolveBundleUrl("~/Templates/htm"), function(templates) {
  $("body").append(templates);
});
A little bit more sophisticated solution is to write html extension for Razor view to get and render template files:
public static class HtmlHelperExtension
{
  public static IHtmlString RenderTemplates(this HtmlHelper htmlHelper, string src)
  {
    var context = htmlHelper.ViewContext.HttpContext;
    if (string.IsNullOrEmpty(src) || context == null || context.Request.Url == null)
    {
      return null;
    }

    var templateUrl = BundleTable.Bundles.ResolveBundleUrl(src);
    var absoluteUrl = new Uri(context.Request.Url, templateUrl);
    var request = WebRequest.Create(absoluteUrl);
    var response = request.GetResponse();
    var stream = response.GetResponseStream();
    if (stream == null)
    {
      return null;
    }

    var data = new StringBuilder();
    using (var sr = new StreamReader(stream))
    {
      string line;
      while ((line = sr.ReadLine()) != null)
      {
        data.AppendLine(line);
      }
    }

    return htmlHelper.Raw(data);
  }
}
Usage:
@Html.RenderTemplates("~/Templates/htm")
As you can see Bundling helps to keep external templates while getting a good design-time experience.