2016-12-04
How to Pass HttpContext to Ninject in ASP.NET MVC
blogentry, programming, aspnetmvc, httpcontext
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.