I have an MVC application that contains a static class providing a boolean value to the rest of the program. My issue is that I want the boolean to reset with each call to the back-end. I want it to be "stateless", getting initialized to false for each and every back-end call. I'll then set it to true as needed. For instance, consider the following where from the Index view we load the About view and then afterwards load the Contact view ...
public static class BooleanProvider
{
public static bool theBool = false;
}
public class HomeController : Controller
{
public ActionResult Index()
{
return View();
}
public ActionResult About()
{
ViewBag.Message = "Your application description page.";
BooleanProvider.theBool = true;
return View();
}
public ActionResult Contact()
{
ViewBag.Message = "Your contact page.";
//I want BooleanProvider.theBool to be false here, since it's a different call, even though we set it to true in the About() method
return View();
}
}
When I check BooleanProvider.theBool at the commented line, it's set to true. Is there a way to "re-initialize" the boolean with every call? I'd rather not make the class instantiable and have to new it up with each controller call, but maybe there is no other way?
Thank you in advance!
You have to use Dependency injection for that.
dotnetcore solution
Scoped Lifetime is exactly what you needed.
https://learn.microsoft.com/en-us/aspnet/core/fundamentals/dependency-injection?view=aspnetcore-3.1#scoped
At your Startup.cs
public void ConfigureServices(IServiceCollection services)
{
// ...
services.AddScoped<BooleanProvider>();
// ...
}
and at your HomeController
public class HomeController : Controller
{
private readonly BooleanProvider _booleanProvider;
public HomeController (BooleanProvider booleanProvider){
_booleanProvider = booleanProvider;
// ...
}
// ...
}
not core solution
Here is a guide if you do not use dotnetcore https://learn.microsoft.com/en-us/aspnet/mvc/overview/older-versions/hands-on-labs/aspnet-mvc-4-dependency-injection
PerRequestLifetimeManager is what you needed here
Install-Package Unity.Mvc3
At Global.asax
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
RegisterGlobalFilters(GlobalFilters.Filters);
RegisterRoutes(RouteTable.Routes);
Bootstrapper.Initialise();
}
public static class Bootstrapper
{
public static void Initialise()
{
var container = BuildUnityContainer();
DependencyResolver.SetResolver(new UnityDependencyResolver(container));
}
private static IUnityContainer BuildUnityContainer()
{
var container = new UnityContainer();
Func<LifetimeManager> LM = () => { return new PerRequestLifetimeManager(); };
container.RegisterType<IBooleanProvider, BooleanProvider>(LM());
return container;
}
}
public class HomeController : Controller
{
private readonly IBooleanProvider _booleanProvider;
public HomeController (IBooleanProvider booleanProvider){
_booleanProvider = booleanProvider;
// ...
}
// ...
}
Related
I need to automatically add api/ prefix to every end point in my asp .net core web API. How to do that?
You can custom MvcOptionsExtensions to set route prefix globally instead of change the route attribute manually.
1.custom MvcOptionsExtensions:
public static class MvcOptionsExtensions
{
public static void UseRoutePrefix(this MvcOptions opts, IRouteTemplateProvider routeAttribute)
{
opts.Conventions.Add(new RoutePrefixConvention(routeAttribute));
}
public static void UseRoutePrefix(this MvcOptions opts, string
prefix)
{
opts.UseRoutePrefix(new RouteAttribute(prefix));
}
}
public class RoutePrefixConvention : IApplicationModelConvention
{
private readonly AttributeRouteModel _routePrefix;
public RoutePrefixConvention(IRouteTemplateProvider route)
{
_routePrefix = new AttributeRouteModel(route);
}
public void Apply(ApplicationModel application)
{
foreach (var selector in application.Controllers.SelectMany(c => c.Selectors))
{
if (selector.AttributeRouteModel != null)
{
selector.AttributeRouteModel = AttributeRouteModel.CombineAttributeRouteModel(_routePrefix, selector.AttributeRouteModel);
}
else
{
selector.AttributeRouteModel = _routePrefix;
}
}
}
}
2:Register in Startup.cs(version before .Net6) or in Program.cs(version beyond .Net 6):
services.AddControllers(o =>{
o.UseRoutePrefix("api");
});
Or:
builder.Services.AddControllers(o =>{
o.UseRoutePrefix("api");
});
Make your controller constructor with Route Prefix "api/"
For example lets say your controller class name is CustomerController
[Route("api/[controller]")]
public class CustomerController : ControllerBase
{
}
// This will become api/customer
[HttpGet]
public async Task<ActionResult> GetCustomers()
{
// Code to get Customers
}
// This will become api/customer/{id}
[HttpGet]
[Route("{id}")]
public async Task<ActionResult> GetCustomerById(int id)
{
// Code to get Customer by Id
}
we can simply add that in top of the controller like this
[Route("api/[controller]")]
public class TestController : ControllerBase
{
[HttpGet("version")]
public IActionResult Get()
{
return new OkObjectResult("Version One");
}
[HttpGet("Types")]
public IActionResult GetTypes()
{
return new OkObjectResult("Type One");
}
}
so that you can access like below
....api/Test/version
....api/Test/Types
Seems you can use a constant.
public static class Consts
{
public const string DefaultRoute = "api/[controller]";
}
and re-use it everywhere. If you need to change the default route everywhere - just change the constant.
[Route(Consts.DefaultRoute)]
public class TestController : ControllerBase
{
...
}
I am trying to start a new web project and I am new to asp.net mvc. Everytime I debug the following code, the error appears stating
the current type, BBNepal.Repository.Interface.IUnitOfWork, is an interface and cannot be constructed.
Are you missing a type mapping?'
Where did I go wrong?
This is my controller
public class RegisterController : Controller
{
private IRegisterService registerService;
public RegisterController(IRegisterService _registerService)
{
registerService = _registerService;
}
// GET: Register
public ActionResult Index()
{
return View();
}
[HttpPost]
public ActionResult Create(RegisterDTO registerDTO)
{
try
{
bool isSaved;
if (registerDTO.Id == 0)
isSaved = registerService.Create(registerDTO);
else
return View();
}
This is my service layer
public interface IRegisterService
{
bool Create(RegisterDTO registerDTO);
}
#endregion
#region Implementation
public class RegisterService : IRegisterService
{
private IUnitOfWork unitOfWork;
public RegisterService(IUnitOfWork _unitOfWork)
{
unitOfWork = _unitOfWork ?? new UnitOfWork();
}
public bool Create(RegisterDTO registerDTO)
{
Register register = registerDTO.Convert();
unitOfWork.RegisterRepository.Insert(register);
unitOfWork.Save();
return true;
}
}
This is my interfaced repository
public interface IUnitOfWork
{
#region Core Method
int Save();
Task<int> SaveAsync();
#endregion
IRepository<Register> RegisterRepository { get; }
}
This is my repository
private IRepository<Register> _registerRepository;
public IRepository<Register> RegisterRepository
{
get
{
return _registerRepository ?? (_registerRepository = new RepositoryBase<Register>(_context));
}
}
My DI registrations are:
public static void RegisterTypes(IUnityContainer container)
{
container.RegisterType<IRegisterService, RegisterService>();
DependencyResolver.SetResolver(new UnityDependencyResolver(container));
}
You need to update the registrations, and include one for IUnitOfWork as it is passed into the constructor of class RegisterService.
It should be something like:
public static void RegisterTypes(IUnityContainer container)
{
container.RegisterType<IRegisterService, RegisterService>();
container.RegisterType<IUnitOfWork, UnitOfWork>();
DependencyResolver.SetResolver(new UnityDependencyResolver(container));
}
This is what i want to be able to do (passing interface(s) to Controllers):
public class TestController : Controller
{
// GET: Test
[HttpGet]
public ActionResult Index(ITestService service)
{
var test = new TestModel();
test.Greeting = "yo" + service.GetString();
test.Name = "nils";
return View(test);
}
}
This is what i have put in Global.asax.cs in the Application_Start() to try to make that work:
// Create a new Unity dependency injection container
var unity = new UnityContainer();
unity.RegisterType<ITestService,TestService>();
// Finally, override the default dependency resolver with Unity
DependencyResolver.SetResolver(new IoCContainer(unity));
I have also, as you can see, created an IoCContainer class which looks as follows:
public class IoCContainer : IDependencyResolver
{
private readonly IUnityContainer _container;
public IoCContainer(IUnityContainer container)
{
_container = container;
}
public object GetService(Type serviceType)
{
if (_container.IsRegistered(serviceType))
return _container.Resolve(serviceType);
return null;
}
public IEnumerable<object> GetServices(Type serviceType)
{
if (_container.IsRegistered(serviceType))
return _container.ResolveAll(serviceType);
return new List<object>();
}
public void Dispose()
{
_container.Dispose();
}
}
When i try to access the "http://humptidumptiurl/Test" it tells me:
A public action method 'Login' was not found on controller 'Companyname.Product.Web.Controllers.TestController'.
Now... i thought it should resolve the ITestService.. not bother about a completely different Controller? other controllers that does not use Unity yet, work as they have always done....
Inputs on how i could achieve my desired solution would be greatly appriciated
EDIT:
Thank you! Of course it injects through the constructor... I should have thought of that... but now it gives me this error message:
{"An error occurred when trying to create a controller of type 'Stimline.Xplorer.Web.Controllers.TestController'. Make sure that the controller has a parameterless public constructor."}
Edited testController:
public class TestController : Controller
{
private readonly ITestService _testService;
public TestController(ITestService service)
{
_testService = service;
}
// GET: Test
[HttpGet]
public ActionResult Index()
{
var test = new TestModel();
test.Greeting = "yo" + _testService.GetString();
test.Name = "nils";
return View(test);
}
}
You're injecting your dependency into your action method.
When using IDependencyResolver in this manner you tend to inject dependencies into your constructor.
Try changing controller to look something like this:
public class TestController : Controller
{
private readonly ITestService service;
public TestController(ITestService service)
{
this.service = service;
}
// GET: Test
[HttpGet]
public ActionResult Index()
{
var test = new TestModel();
test.Greeting = "yo";
test.Name = "nils";
// TODO do something with ITestService
// this.service.DoSomethingCool()
return View(test);
}
}
Declare it like this :
public class TestController : Controller
{
private ITestService service;
public TestController(ITestService service)
{
this.service = service;
}
// GET: Test
[HttpGet]
public ActionResult Index()
{
var test = new TestModel();
test.Greeting = "yo";
test.Name = "nils";
return View(test);
}
}
Please inject your dependencies inside your constructor. You by mistake passed it to your action method.
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?
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.