2016-12-04

How to Pass HttpContext to Ninject in ASP.NET MVC

blogentry, programming, aspnetmvc, httpcontext

I've been working with ASP.NET MVC websites that creates a connection string based on a query string value.

I've been trying to learn how to use Ninject and decided to inject repository instances to controllers.

I ran into a problem where HttpContext object instance was not available at the time of kernel binding.

I found out a solution and I'd like to share how.

Here is a simple ASP.NET MVC controller that accepts IRepository object instance. "Index" simply returns a view with "HomeIndexViewModel", which accepts a connection string from the repository (this is a very contrived example).

1public class HomeController : Controller2{3		private readonly IRepository _repository;4
5		public HomeController(IRepository repository)6		{7				_repository = repository;8		}9
10		// GET: Home11		public ActionResult Index()12		{13				return View(new HomeIndexViewModel(_repository.ConnectionString));14		}15}

Repository accepts a connection string but it's retrieved via Query String argument of "db" parameter.

1public class Repository : IRepository2{3	public string ConnectionString { get; set; }4
5	public Repository(string connectionString)6	{7		ConnectionString = connectionString;8	}9}

I've created a simplified utility class, which returns a connection string depending on the query string parameter "db".

1public class QueryArgParser2{3	public HttpContext HttpContext { get; set; }4
5	public QueryArgParser(HttpContext httpContext)6	{7		HttpContext = httpContext;8	}9
10	public string GetConnectionString()11	{12		string dbValue = HttpContext.Request.QueryString["db"];13		switch (dbValue)14		{15			case "prod":16				return @"Server=GODDESS\\SQL2014;Initial Catalog=AdventureWorks2014;Integrated Security=SSPI";17			case "stage":18				return @"Server=GODDESS\\SQL2014;Initial Catalog=AdventureWorks2014Stage;Integrated Security=SSPI";19			case "dev":20				return @"Server=GODDESS\\SQL2014;Initial Catalog=AdventureWorks2014Dev;Integrated Security=SSPI";21		}22
23		throw new ArgumentException("Query string doesn't contain "db" parameter");24	}25}

I've installed following Ninject Nuget Packages.

Installing "Ninject.MVC3" will add a file called "NinjectWebCommon" under "App_Start" folder.

You can now register your dependencies in a method called "RegisterServices" within "NinjectWebCommon" class.

1private static void RegisterServices(IKernel kernel)2{3	const string dataAccessParameterName = "connectionString";4	Func<HttpContext, string> getConnectionString =5		context => context != null ?6                    new QueryArgParser(context).GetConnectionString() :7                    string.Empty;8
9	kernel.Bind<IRepository>()10		.To<Repository>()11		.WithConstructorArgument(dataAccessParameterName,12			ninjectContext => getConnectionString(HttpContext.Current));13}

Line 9~13 binds "IRepository" to "Repository", which requires a contructor argument of connection string. "getConnectionString(...)" in line 4~7 is just to make the code more readable.

What's important here is the line 12~13, which is the callback within "WithConstructorArgument" that is called during RunTime, therefore "HttpContext.Current" is not null.

Now set the web start page to "?db=prod", "?db=stage", or "?db=dev"

You will see the connection string on the web page as shown below (this is for "?db=prod")

I've found the answer via StackOverflow answered by "Ruben Bartelink". One line of code Ruben posted saved me hours of headache.

The working source code is available on GitHub.