NInject Singletons - Are they created at application or session level? - c#

I'm using NInject in my ASP.NET MVC application and I'm not 100% sure how the singleton is working when creating my Object context.
My Question is:
Using the code below will there be one
ObjectContext per user session or will
there be one that is share for the
entire application? I want each user
to have only one context at one time,
but each user must have their own
instance.
Is InRequestScope() something I should be considering?
I also do the same thing with a WCF service and I assume the answer will be same for both.
My Global.asax:
public class MvcApplication : NinjectHttpApplication
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Change", action = "Index", id = UrlParameter.Optional } // Parameter defaults
);
}
protected override void OnApplicationStarted()
{
// Ninject Code
base.OnApplicationStarted();
AreaRegistration.RegisterAllAreas();
RegisterRoutes(RouteTable.Routes);
}
protected override IKernel CreateKernel()
{
var modules = new INinjectModule[] { new ContextModule() };
return new StandardKernel(modules);
}
public class ContextModule : NinjectModule
{
public override void Load()
{
Bind<ObjectContext>().To<ChangeRoutingEntities>().InSingletonScope();
Bind<IObjectContext>().To<ObjectContextAdapter>();
Bind<IUnitOfWork>().To<UnitOfWork>();
}
}
}

ISingletonScope is an application wide scope.
InRequestScope is only for the current request.
You need a session scope. See http://iridescence.no/post/Session-Scoped-Bindings-With-Ninject-2.aspx for a way to implement this type of scope.
public static class NinjectSessionScopingExtention
{
public static void InSessionScope<T>(this IBindingInSyntax<T> parent)
{
parent.InScope(SessionScopeCallback);
}
private const string _sessionKey = "Ninject Session Scope Sync Root";
private static object SessionScopeCallback(IContext context)
{
if (HttpContext.Current.Session[_sessionKey] == null)
{
HttpContext.Current.Session[_sessionKey] = new object();
}
return HttpContext.Current.Session[_sessionKey];
}
}

Related

Adding Unity to MVC WebApi app

Hello all I've Added Unity to an MVC app. it seems that DI is working with the MVC Portion of the app but i cannot figure out why it wont work with the API part of the application.
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name:"DefaultApi",
routeTemplate:"api/{controller}/{id}",
defaults: new {id = RouteParameter.Optional}
);
config.Formatters.Add(new JsonMediaTypeFormatter());
config.Formatters.JsonFormatter.SerializerSettings.ReferenceLoopHandling =
Newtonsoft.Json.ReferenceLoopHandling.Ignore;
config.Formatters.JsonFormatter.SerializerSettings.Formatting = Newtonsoft.Json.Formatting.None;
}
}
public static class UnityMvcActivator
{
/// <summary>
/// Integrates Unity when the application starts.
/// </summary>
public static void Start(HttpConfiguration configuration)
{
FilterProviders.Providers.Remove(FilterProviders.Providers.OfType<FilterAttributeFilterProvider>().First());
FilterProviders.Providers.Add(new UnityFilterAttributeFilterProvider(UnityConfig.Container));
DependencyResolver.SetResolver(new UnityDependencyResolver(UnityConfig.Container));
// TODO: Uncomment if you want to use PerRequestLifetimeManager
// Microsoft.Web.Infrastructure.DynamicModuleHelper.DynamicModuleUtility.RegisterModule(typeof(UnityPerRequestHttpModule));
}
/// <summary>
/// Disposes the Unity container when the application is shut down.
/// </summary>
public static void Shutdown()
{
UnityConfig.Container.Dispose();
}
}
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Address", action = "Index", id = UrlParameter.Optional }
);
}
}
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
GlobalConfiguration.Configure(WebApiConfig.Register);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
BundleConfig.RegisterBundles(BundleTable.Bundles);
RouteConfig.RegisterRoutes(RouteTable.Routes);
UnityMvcActivator.Start(GlobalConfiguration.Configuration);
}
I am just trying to get a repository to work in the web api but i keep getting
An error occurred when trying to create a controller of type 'AddressSearchController'. Make sure that the controller has a parameterless public constructor.",
this error. I have seen a few posts about this. I have tried them and still cannot get this to work. Does anyone have any suggestions?
public class AddressSearchController:_SimpleController<Address>
{
public AddressSearchController(IRepository<Address> addressRepository) : base(addressRepository)
{
}
[HttpPost]
[Route("api/AddressSearch/Search")]
public IHttpActionResult Search([FromBody] AddressSearchDto addressSearchDto)
{
var addresses = new List<Address>()
{
CreateAddress(1,"Main St", 123),
CreateAddress(2,"Main St", 124),
CreateAddress(3,"Main St", 125),
};
return Ok(addresses);
}
static Address CreateAddress(int id,string street, int houseNumber)
{
return new Address()
{
Id = id,
StreetName = street,
HouseNumber = houseNumber
};
}
}
public static void RegisterTypes(IUnityContainer container)
{
RegisterInstances(container);
}
private static void RegisterInstances(IUnityContainer container)
{
container.RegisterType<IAddressContext, AddressContext>();
container.RegisterType(typeof(IRepository<>), typeof(Repository<>));
}
using System;
using Address_Tracker.Data.Context;
using Address_Tracker.Data.Context.Interfaces;
using Address_Tracker.Data.Repositories;
using Unity;
namespace Address_Tracker
{
/// <summary>
/// Specifies the Unity configuration for the main container.
/// </summary>
public static class UnityConfig
{
#region Unity Container
private static Lazy<IUnityContainer> container =
new Lazy<IUnityContainer>(() =>
{
var container = new UnityContainer();
RegisterTypes(container);
return container;
});
/// <summary>
/// Configured Unity Container.
/// </summary>
public static IUnityContainer Container => container.Value;
#endregion
/// <summary>
/// Registers the type mappings with the Unity container.
/// </summary>
/// <param name="container">The unity container to configure.</param>
/// <remarks>
/// There is no need to register concrete types such as controllers or
/// API controllers (unless you want to change the defaults), as Unity
/// allows resolving a concrete type even if it was not previously
/// registered.
/// </remarks>
public static void RegisterTypes(IUnityContainer container)
{
RegisterInstances(container);
}
private static void RegisterInstances(IUnityContainer container)
{
container.RegisterType<IAddressContext, AddressContext>();
container.RegisterType(typeof(IRepository<>), typeof(Repository<>));
}
}
}
Your AddressSearchController should have a default constructor, or if you have no default / parameterless constructor, then please pass interfaces instead of concrete classes in AddressSearchController.
I believe you may have such a scenario:
public class AddressSearchController : ApiController
{
public AddressSearchController(SomeClassParameter obj)
{
//some code
}
}
What you want actually is ether this:
public class AddressSearchController : ApiController
{
public AddressSearchController() // add default ctor
{
}
public AddressSearchController(SomeClassParameter obj)
{
//some code
}
}
or this:
Register the interface ISomeClassParameter for type SomeClassParameter in Unity
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
UnityConfig.RegisterComponents(); // <----- Add this line
GlobalConfiguration.Configure(WebApiConfig.Register);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
}
Register component:
container.RegisterType<ISomeClassParameter , SomeClassParameter >();
and do constructor injection :
public class AddressSearchController : ApiController
{
public AddressSearchController(ISomeClassParameter obj)
{
//some code
}
}
Also, make sure you have the WebApi version of Unity
I tried it out, it worked for me:

Can't write to response in Application_Start

In my Application_Start method I'm doing some setup and logging to my database. I had an issue where my connectionstring was wrong, which is not a big deal but I'd like to validate the database is available during Application_Start() and report back to the user if it's down.
Since the httpResponse isn't yet available I can't write something to the browser.
What other practical options do I have?
Here's a quick implementation of my suggestion.
Modify global.asax to have a public variable
public class MvcApplication : System.Web.HttpApplication
{
public static bool IsConfigured { get; set; }
Set IsConfigured = true as you leave Application_Start if everything is configured
Then add a ActionFilter like this
public class ConfiguredAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
if (MvcApplication.IsConfigured) return;
filterContext.Result = new ViewResult
{
ViewName = "Offline",
TempData = filterContext.Controller.TempData
};
}
}
Create your Offline view in the Shared views folder
Register your new filter
public class FilterConfig
{
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new ConfiguredAttribute());
}
}

mvc app on production server - ninject, routing, areas

The problem is with my app in production environment. When I publish my MVC app on production server ninject stops working, and page page loads about 30s, same time takes controller method to just throw back true/false(ajax query). Unfortunately, only information that administrator of hosting company gave me was:log from IIS
My application has:
MVC project(4) - EntityFramework + Ninject,
Important thing is that my LandingPage which I would like to show at the
beginning is in Area called "LandingPage" with controller named "Landing"
Repository project
DB project with interfaces to models
My RouteConfig is:
var route = routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Landing", action = "Index", id =
UrlParameter.Optional }
).DataTokens = new RouteValueDictionary(new { area = "LandingPage"}
);
I thought that problem sits in Ninject configuration, but after today's info from hosting company I don't have any clue what to do with this problem.
Edit:
My RegisterService:
private static void RegisterServices(IKernel kernel)
{
kernel.Bind<IEmailRepository>().To<EmailRepository>();
}
My Controller:
private readonly IEmailRepository repository;
public LandingController(IEmailRepository repository)
{
this.repository = repository;
}
EmailRepository:
public class EmailRepository : IEmailRepository
{
ReliableDB context;
public EmailRepository(ReliableDB context)
{
this.context = context;
}
public void InsertOrUpdate(string emailAddress)
{
LPEmail item = new LPEmail();
item.Date = DateTime.Now.Date;
item.Email_Address = emailAddress;
context.LPEmail.Add(item);
}
public bool EmailExists(string emailAddress)
{
bool result = context.LPEmail.Any(w => w.Email_Address == emailAddress);
return result;
}
public void Save()
{
context.SaveChanges();
}
public void Dispose()
{
context.Dispose();
}
}

Getting started with Ninject

I watched the first 2 beginner tutorials for Ninject on dimecasts.net. Now, I want to use Ninject 2.2 in ASP.NET MVC 3. I want a view with a mocked out Model. I get object reference not set to an instance of an object when calling my service;
public class HomeController : Controller
{
private readonly IMilestoneService _service;
public HomeController()
{
}
HomeController(IMilestoneService service)
{
_service = service;
}
public ActionResult Index()
{
ViewBag.Message = "Change Request System";
return View();
}
public ActionResult About()
{
return View();
}
#region Partial views
public ActionResult Milestone()
{
var result = _service.GetMileStones();//OBJECT REF ERROR
return View(result);
}
#endregion
}
//####GLOBAL.ASAX
//By using the NinjectHttpApplication, it automatically takes care of controllers, starting up mvc, etc.
//Ninject.Web.Mvc
public class MvcApplication : NinjectHttpApplication
{
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new HandleErrorAttribute());
}
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
);
}
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
RegisterGlobalFilters(GlobalFilters.Filters);
RegisterRoutes(RouteTable.Routes);
//StartNinject();
}
#region Inversion of Control
protected override IKernel CreateKernel()
{
return Container;
}
static IKernel _container;
public static IKernel Container
{
get
{
if (_container == null)
{
_container = new StandardKernel(new SiteModule());
}
return _container;
}
}
internal class SiteModule : NinjectModule
{
public override void Load()
{
//Set up ninject bindings here.
Bind<IMilestoneService>().To<MileStoneService>();
}
}
#endregion
}
I'm using Razor, he's the milestone partial view
#foreach (var item in Model)
{
<div>item.Name</div>
}
Finally, the Home view Index
#{
ViewBag.Title = "Home Page";
}
<h2>#ViewBag.Message</h2>
<p>
#Html.Action("Milestone");
</p>
Edit 11/20/2013
Note that Ninject has since released version 2.0. The changes are nicely outlined on their site. Of Note StandardModule is now NinjectModule and namespace Ninject.Core no longer exists. I was able to replace it with just Ninject.
There is an issue with your controller class, the constructor with the dependency is private. Your controller should look like:
public class HomeController : Controller
{
private readonly IMilestoneService _service;
public HomeController(IMilestoneService service)
{
_service = service;
}
}
Don't even include a public parameterless constructor, it isn't even valid, your class needs that dependency to function.
In fact, I also insert a null check against that dependency in the constructor just to be sure my class is valid on construction:
public class HomeController : Controller
{
private readonly IMilestoneService _service;
public HomeController(IMilestoneService service)
{
_service = service;
Enforce.NotNull(() => _service); // lambda to auto-magically get variable name for exception
}
}
There also may be an issue with your MvcApplication class.
Instead of protected void Application_Start(), there is a different function you can override, protected override void OnApplicationStarted()
This is where your calls to setup routing should go:
public class MvcApplication : NinjectHttpApplication
{
public override void Init()
{
base.Init();
Mappers.Initialize();
}
protected override Ninject.IKernel CreateKernel()
{
return Ioc.Initialize();
}
protected override void OnApplicationStarted()
{
AreaRegistration.RegisterAllAreas();
RegisterRoutes(RouteTable.Routes);
}
public static void RegisterRoutes(RouteCollection routes)
{
Routing.RegisterRoutes(routes);
//RouteDebug.RouteDebugger.RewriteRoutesForTesting(RouteTable.Routes);
}
}
Of course, if you are already calling Application_Start that's fine too, but I didn't see it in the OP.
Try this in your global.asax file:
//By using the NinjectHttpApplication, it automatically takes care of controllers, starting up ninject, etc.
//Ninject.Web.Mvc
public class MvcApplication : NinjectHttpApplication
{
//Your other stuff here. No need to call StartNinject().
#region Inversion of Control
protected override IKernel CreateKernel()
{
return Container;
}
static IKernel _container;
public static IKernel Container
{
get
{
if (_container == null)
{
_container = new StandardKernel(new SiteModule());
}
return _container;
}
}
internal class SiteModule : NinjectModule
{
public override void Load()
{
//Set up ninject bindings here.
Bind<IMilestoneService>().To<MileStoneService>();
}
}
#endregion
}
I believe if Ninject couldn't bind that interface, you'd get a binding error. This makes me think Ninject is not instantiating your controller.
Have you included Ninject.Web.Mvc?

MVC3 + Ninject - How to?

I've just started playing with IoC containers and therefore chosed Ninject.
After several hours of sweat and tears I still cant figure out how to setup my MVC3 application with Ninject.
So far I have:
Global.asax.cs
public class MvcApplication : Ninject.Web.Mvc.NinjectHttpApplication
{
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new HandleErrorAttribute());
}
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
);
}
protected void Application_Start()
{
DependencyResolver.SetResolver(new MyDependencyResolver(CreateKernel()));
RegisterGlobalFilters(GlobalFilters.Filters);
RegisterRoutes(RouteTable.Routes);
}
protected override IKernel CreateKernel()
{
var modules = new [] { new ServiceModule() };
return new StandardKernel(modules);
}
}
ServiceModule.cs
internal class ServiceModule : NinjectModule
{
public override void Load()
{
Bind<IGreetingService>().To<GreetingService>();
}
}
MyDependencyResolver.cs
public class MyDependencyResolver : IDependencyResolver
{
private IKernel kernel;
public MyDependencyResolver(IKernel kernel)
{
this.kernel = kernel;
}
public object GetService(System.Type serviceType)
{
return kernel.TryGet(serviceType);
}
public System.Collections.Generic.IEnumerable<object> GetServices(System.Type serviceType)
{
return kernel.GetAll(serviceType);
}
}
GreetingService.cs
public interface IGreetingService
{
string Hello();
}
public class GreetingService : IGreetingService
{
public string Hello()
{
return "Hello from GreetingService";
}
}
TestController.cs
public class TestController : Controller
{
private readonly IGreetingService service;
public TestController(IGreetingService service)
{
this.service = service;
}
public ActionResult Index()
{
return View("Index", service.Hello());
}
}
Each time I try to load the Index view it either just throws a overflow exception or a HTTP 404 error - If I remove all the Ninject code it works perfectly, whats wrong?
You are mixing an own dependency resolver with the MVC extension. I'd suggest either going with your own dependency resolver or with using the MVC extension but not both. When using the MVC extension you have to use OnApplicationStarted instead of Application_Start.
See http://www.planetgeek.ch/2010/11/13/official-ninject-mvc-extension-gets-support-for-mvc3/ and have a look at the SampleApplication that comes with the source code of the MVC extension https://github.com/ninject/ninject.web.mvc.
Also the fix is not used anymore when you use the current version for the build server: http://teamcity.codebetter.com
UPDATE: The Ninject.MVC3 package continues to be updated and works OOTB against MVC4 RTM (and RC). See this page in the wiki for details.

Categories

Resources