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.

2012-02-14

git-svn merging

Recently I have faced some strange git merging issue.
My team uses git and we have our own git and build servers and we are happy about that. But our mysterious deployment system is still configured to use svn (shame on us). Besides other guys also work with svn. Svn server has three years of history and is used for deployment. My team don't really need all the history so we just made "svn export" of the latest revision and uploaded sources to the git server.
It is great, history is not messy, a successful Git branching model and so on. But there is still a necessity to merge changes back to svn when sprint is finished. And here the problem comes...
Even thought "svn" and "git" team didn't have a lot of conflicting files, merge back using git-svn was inexplicably hard: when we exported svn branch to git from same starting revision and tried to merge two branches we got conflicts for all changed files, but not only real conflicts. It was also hard to solve conflicts because there were not any base version of files, two-way merge only. The problem is that we made just svn export without any history so git and git-svn branches don't have anything common.
This is very odd since git is tracking file content but not a structure, isn't it?
The solution is tree following steps:
1. Merge the git-svn starting point commit to the git branch:
git checkout master
git merge -s ours git_svn_starting_point_commit_id
This merge won't do anything but interconnect git and git-svn branches so they are not totally different any more.
2. Main merge git branch to svn:
git checkout git-svn-trunk
git merge --squash master
At this point you would probably have some conflicts, but there are much less files and now you would have a base version so it is possible to use tree-way merge while solving conflicts.
3. Commit you changes to svn:
git commit -a -m "Merge form git master."
git svn rebase
git svn dcommit
Done!
Using two different version control systems is quite painful so just use git and be happy :-)