I am trying to use the CookieTempDataProvider in MVC 3 futures assembly. I believe I have "wired" it up successfully using ninject. Below is the code from my app_start.cs file:
[assembly: WebActivator.PreApplicationStartMethod(typeof(Web.AppStart), "Start")]
namespace Web {
public static class AppStart {
public static void RegisterServices(IKernel kernel) {
kernel.Bind<ITempDataProvider>().To<CookieTempDataProvider>();
}
public static void Start() {
// Create Ninject DI Kernel
// IKernel kernel = new StandardKernel();
IKernel kernel = Container;
// Register services with our Ninject DI Container
RegisterServices(kernel);
// Tell ASP.NET MVC 3 to use our Ninject DI Container
DependencyResolver.SetResolver(new NinjectServiceLocator(kernel));
}
static IKernel _container;
public static IKernel Container
{
get
{
if (_container == null)
_container = new StandardKernel();
return _container;
}
}
However, when I access my page that uses TempData, I get the this error indicating that it is still trying to use the SessionTempDataProvider:
Server Error in '/' Application.
The SessionStateTempDataProvider class requires session state to be enabled.
Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.
Exception Details: System.InvalidOperationException: The SessionStateTempDataProvider class requires session state to be enabled.
I must be missing something and I can't figure out what it is. Any help would be most appreciated.
Many Thanks
I've only been able to get this to work with the BaseController approach. The controller creation processes does't ask Ninject for a ITempDataProvider.
public class BaseController : Controller
{
protected override ITempDataProvider CreateTempDataProvider()
{
return new CookieTempDataProvider(HttpContext);
}
}
Extend the controller class
public static void SetMessage(this Controller controller, String message)
{
controller.TempData["Messag"] = message;
}
Then you can use it like this:
public ActionResult Save()
{
//Validation...
Save(Foo);
this.SetMessage("Item saved successfully");
return Redirect("/Site");
}
No number three :)
Related
Good afternoon, I created my interface in my asp net core mvc 6 project. I use this interface in the EmployeeController controller, when I try to go to the view that implements my controller, I get this error. Do not tell me what is the problem?
Interfaces:
namespace WebProductionAccounting.DAL.Interfaces
{
public interface IBaseRepository<T>
{
bool Create(T entity);
T GetValue(int id);
Task<List<T>> GetAll();
bool Delete(T entity);
}
}
using WebProductionAccounting.Domain.Entities;
namespace WebProductionAccounting.DAL.Interfaces
{
public interface IEmployeeRepository:IBaseRepository<Employee>
{
Employee GetByFullname(string firstname,string lastname,string middlename);
}
}
Controller:
using Microsoft.AspNetCore.Mvc;
using WebProductionAccounting.DAL.Interfaces;
namespace WebProductionAccounting.Controllers
{
public class EmployeeController : Controller
{
private readonly IEmployeeRepository _employeeRepository;
public EmployeeController(IEmployeeRepository employeeRepository)
{
_employeeRepository = employeeRepository;
}
[HttpGet]
public async Task<IActionResult> GetEmployees()
{
var response = await _employeeRepository.GetAll();
return View(response);
}
}
}
Program.cs:
using Microsoft.EntityFrameworkCore;
using WebProductionAccounting.DAL;
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddControllersWithViews();
// Get connection with pssql db
var connection = builder.Configuration.GetConnectionString("DefaultConnection");
// Reg DbConext pssql
builder.Services.AddDbContext<AppDbContext>(options =>
options.UseNpgsql(connection));
// Add Npgsql.EnableLegacyTimestampBehavior and Npgsql.DisableDateTimeInfinityConversions in DbContext
AppContext.SetSwitch("Npgsql.EnableLegacyTimestampBehavior", true);
AppContext.SetSwitch("Npgsql.DisableDateTimeInfinityConversions", true);
var app = builder.Build();
// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Home/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
app.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
app.Run();
When going to the GetEmployees view in my web application, an error occurs:
InvalidOperationException: Unable to resolve service for type 'WebProductionAccounting.DAL.Interfaces.IEmployeeRepository' while attempting to activate 'WebProductionAccounting.Controllers.EmployeeController'.
Microsoft.Extensions.DependencyInjection.ActivatorUtilities.GetService(IServiceProvider sp, Type type, Type requiredBy, bool isDefaultParameterRequired)
Tried to register services through AddTransient,AddScoped but did not understand where exactly to register in my main project file.
Add the following class
public class EmployeeRepository : IEmployeeRepository
{
public bool Create(Employee entity)
{
throw new NotImplementedException();
}
public bool Delete(Employee entity)
{
throw new NotImplementedException();
}
public Task<List<Employee>> GetAll()
{
throw new NotImplementedException();
}
public Employee GetByFullname(string firstname, string lastname, string middlename)
{
throw new NotImplementedException();
}
public Employee GetValue(int id)
{
throw new NotImplementedException();
}
}
and then After implementing the methods,add
builder.Services.AddScoped<IEmployeeRepository, EmployeeRepository>();
in program.cs
InvalidOperationException: Unable to resolve service for type
'WebProductionAccounting.DAL.Interfaces.IEmployeeRepository'
Microsoft.Extensions.DependencyInjection.ActivatorUtilities.GetService(IServiceProvider
sp, Type type, Type requiredBy, bool isDefaultParameterRequired).Tried
to register services through AddTransient,AddScoped but did not
understand where exactly to register in my main project file.
Well, the error you are getting is pretty obvious and straight forward. As you are learning so let me explain more details reagrding your exception.
If you would seen the exception message, its pointing you that you haven't register IEmployeeRepository interface because you are trying to inject IEmployeeRepository within your EmployeeController.
Why The Exception For:
In asp.net core you might hear a concept Dependency injection which is a technique for achieving Inversion of Control (IoC) between classes and their dependencies.
What's more is, while you are creating any service class for your scenario the EmployeeRepository you need to tell the compiler to build that service. In asp.net core we call it to register a service that's is in program.cs we need to AddScoped for your EmployeeRepository.
Register Your Dependency:
So to resolve your exception you can write as following:
builder.Services.AddScoped<IEmployeeRepository, EmployeeRepository>();
Output:
Note: As you can observe in above example I have reproduce your exception, I have IUnitOfWork service interface. However, I haven't resgiter that in program.cs file consequently, I got the error. You can see below:
If you would like to know more details on dependency inject and service life time you could check our official document here.
I have a custom ASP.NET MVC controller that retrieves operations from the user service. I want to pass the operations property to the scenario service using dependency injection.
public abstract class BaseController : Controller {
protected IUserService userService;
public OperationWrapper operations { get; private set; }
public BaseController(IUserService userService) {
this.userService = userService;
this.operations = userService.GetOperations(HttpContext.Current.User.Identity.Name);
}
}
public abstract class ScenarioController : BaseController {
protected IScenarioService scenarioService;
public ScenarioController(IScenarioService scenarioService, IUserService userService)
: base(userService) {
this.scenarioService = scenarioService;
}
}
public class ScenarioService : IScenarioService {
private OperationWrapper operations;
public ScenarioService(OperationWrapper operations) {
this.repo = repo;
this.operations = operations;
}
}
Here is my Windsor installer.
public class Installer : IWindsorInstaller {
public void Install(IWindsorContainer container, IConfigurationStore store) {
container.Register(Classes.FromThisAssembly()
.BasedOn<IController>());
container.Register(Classes.FromThisAssembly()
.Where(x => x.Name.EndsWith("Service"))
.WithService.DefaultInterfaces()
.LifestyleTransient());
}
}
I pretty sure I've done something similar with Ninject a couple of years back. What do I need to add to the installer in order to make this work? Is it even possible?
There are a few of options here:
1. Use LifeStylePerWebRequest() and UsingFactoryMethod()
First, you could register an OperationWrapper as LifestylePerWebRequest() and inject it into both the BaseController and ScenarioService. Windsor will let you register the dependency with a factory method for creating it, which can in turn call other services which have been registered.
container.Register(Component.For<OperationWrapper>()
.LifestylePerWebRequest()
.UsingFactoryMethod(kernel =>
{
var userService = kernel.Resolve<IUserService>();
try
{
return userService.GetOperations(
HttpContext.Current.User.Identity.Name);
}
finally
{
kernel.ReleaseComponent(userService);
}
}));
So, every time Windsor is asked for an OperationWrapper, it will run that call against an instance if IUserService, giving it the Name of the current User. By binding the lifestyle to LifestylePerWebRequest(), you can verify that each request will get its own instance of the OperationWrapper and it won't bleed across requests.
(The only edge case you'd run into is one where a user becomes authenticated mid-request and the OperationWrapper needs to be adjusted as a result. If that's a normal-path use case, this may need some re-thinking.)
Then, modify your base controller to take that registered object in as a dependency:
public abstract class BaseController : Controller {
protected IUserService userService;
protected OperationWrapper operations;
public BaseController(IUserService userService, OperationWrapper operations) {
this.userService = userService;
this.operations = operations;
}
}
2. Use Method Injection
It looks like OperationWrapper is some sort of context object, and those can sometimes be injected into the method instead of into the constructor.
For instance, if your method was:
int GetTransactionId() { /* use OperationWrapper property */ }
You could just modify the signature to look like:
int GetTransactionId(OperationWrapper operations) { /* use arg */ }
In this situation, it makes sense to use it if a small-ish subset of your service's methods use that dependency. If the majority (or totality) of methods need it, then you should probably go a different route.
3. Don't use DI for OperationWrapper at all
In situations where you have a highly-stateful contextual object (which it seems like your OperationWrapper is), it frequently just makes sense to have a property whose value gets passed around. Since the object is based on some current thread state and is accessible from everywhere in any subclassed Controller, it may be right to just keep the pattern you have.
If you can't answer the question "What am I unable to do with OperationWrapper now that DI is going to solve for me?" with anything but "use the pattern/container," this may be the option for this particular situation.
You should set dependency resolver in Application_Start method of global.asax
System.Web.MVC.DependencyResolver.SetResolver(your windsor resolver)
Create a class that inherits from DefaultControllerFactory. Something like this will do:
public class WindsorControllerFactory : DefaultControllerFactory
{
public WindsorControllerFactory(IKernel kernel)
{
_kernel = kernel;
}
protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
{
if (controllerType == null)
{
throw new HttpException(
404,
String.Format(
CultureInfo.CurrentCulture,
"The controller for path '{0}' was not found or does not implement IController.",
requestContext.HttpContext.Request.Path
)
);
}
return (IController)_kernel.Resolve(controllerType);
}
public override void ReleaseController(IController controller)
{
Kernel.ReleaseComponent(controller);
}
private readonly IKernel _kernel;
private IKernel Kernel
{
get { return _kernel; }
}
}
In the Application_Start method of your MvcApplication class add the following:
var container = new WindsorContainer();
container.Install(FromAssembly.This());
ControllerBuilder.Current.SetControllerFactory(
new WindsorControllerFactory(container.Kernel)
);
This should work with your existing installer and get you to the point where Windsor will start resolving your dependencies for you. You might have to fill-in a few gaps, but you'll get the point.
I've borrowed heavily from: https://github.com/castleproject/Windsor/blob/master/docs/mvc-tutorial-intro.md
Be wary of using IDependencyResolver as it doesn't make provision for releasing what's resolved.
I was wondering if there is a more ellegant pattern for using IOC containers than the way suggested in the codeproject example below.
Surely it is is possible to use an IOC container so that controller constructors are parameterless when you have a solid IOC process in place.
It also means when your application has more than MVC eg WEB-API and whatever else , you are building the same type of solution for that technology.
That doesnt look DRY to me.
Is there a nice pattern someone uses to "register" the Container eg as IMyMagicControllerHat and get singleton using some nice .net System library?
Clearly if you have a UI Depends on Core depends on Model type of application, you are concerned about build dependencies and static calls.
The static calls back to MVCApplication concern me. I was looking for a better approach.
The CODEProject link....
http://www.codeproject.com/Articles/99361/How-To-Use-Unity-Container-In-ASP-NET-MVC-Framewor
In short the relevant code....
public interface IContainerAccessor
{
IUnityContainer Container { get; }
}
public class MvcApplication : HttpApplication, IContainerAccessor
{
private static IUnityContainer _container;
public static IUnityContainer Container
{
get { return _container; }
}
public static IUnityContainer Container
{
get { return _container; }
}
Also there are examples like This from MS that shows a custom static as a solution. http://msdn.microsoft.com/en-us/library/vstudio/hh323691%28v=vs.100%29.aspx I was expecting there might be a well established adapter pattern or similar. Im going to have 100s of accesses to the unity container, so getting the pattern right looks important.
Even blogs that make perfect sense like this, dont show how deep in the application you should get a reference to the original container instance.
http://blog.ploeh.dk/2010/09/29/TheRegisterResolveReleasePattern.aspx
I Appreciate any tips on good ways to solve.
That codeproject article was published in 2010 based on MVC1. MVC3 now contains an IDependencyResolver implementation for better integration with IoC. And for MVC3 with Unity you might look at Unity.MVC3
And if you want to avoid constructor injection you could always register Unity as the default IServiceLocator:
ServiceLocator.SetLocatorProvider(() => new UnityServiceLocator(Container));
In case anyone is interested, during a google search:
I ended using a custom Global Class. 1 Static Call to start things off.
The rest as Interface/adapter pattern. The example register Unity and Enterprise Library.
And records important details from authenticated request for logging.
... this is a partial extract only... For illustration purposes
Call from MVC Application Global.asax
public class BosMasterWebApplication : System.Web.HttpApplication
{
private bool IsBootStraped;
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
WebApiConfig.Register(GlobalConfiguration.Configuration);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
AuthConfig.RegisterAuth();
IsBootStraped = false;
}
public override void Init()
{
base.Init();
// handlers managed by ASP.Net during Forms authentication
AuthenticateRequest += new EventHandler(AuthHandler);
PostAuthenticateRequest += new EventHandler(PostAuthHandler);
}
public void AuthHandler(object sender, EventArgs e)
{
// if you need to setup anything
}
public void PostAuthHandler(object sender, EventArgs e)
{
// called several times during the authentication process
if (!IsBootStraped)
BosGlobal.HttpBootStrap(this);
if (Request.IsAuthenticated)
{
// did FormsAuthentication.SetAuthCookie was executed if we get here, so set username
BosGlobal.BGA.IBosCurrentState.CurrentUser.SetFromEnvironment();
}
}
}
/// <summary>
/// Central adapter management. Dont abuse this class
/// </summary>
public static class BosGlobal
{
public static IBosGlobalAdpater BGA { get; private set; }
// set up the adapter
static BosGlobal()
{
BGA = new BosGlobalAdapter( );
}
public static void HttpBootStrap(HttpApplication httpApplication)
{
BGA.BootStrap(httpApplication);
}
public static void LocalBootStrap(string machineName)
{ // the LOCAL WPF bootstrap alternative... no http request exists.
BGA.BootStrap(machineName);
}
}
public class BosGlobalAdapter : IBosGlobalAdpater
{
public static bool IsBootStrapped { get; private set; }
/// Unity Container with key dependencies registered
public IUnityContainer IUnityContainer { get; private set; } //IOC Container
//Adapters
public IBosLogAdapter ILogAdapter { get; private set; }
public IBosExceptionManagerAdapter ExceptionManagerAdapter { get; private set; }
public BosGlobalAdapter()
{
IsBootStrapped = false;
IUnityContainer = new UnityContainer();// one container per work process. managing and resolving dependencies
}
public void BootStrap(HttpApplication httpApplication )
{
IBosCurrentState = new BosCurrentState(httpApplication);
BootStrapEnterpriseLibrary();
BootStrapAdapters();
IsBootStrapped = true;
}
private void BootStrapAdapters()
{
ILogAdapter = new BosLogAdapter(IUnityContainer.Resolve<LogWriter>());
ExceptionManagerAdapter = new BosExceptionManagerAdapter(IUnityContainer.Resolve<ExceptionManager>());
}
private void BootStrapEnterpriseLibrary()
{ // now we tell unity about the container manager inside EntLib.
// we dont want 2 containers, so we tell UNity look after EntLib as well please
IUnityContainer.AddNewExtension<EnterpriseLibraryCoreExtension>();
}
I'm trying to use the [Inject] attribute on a BasicRoleProvider : RoleProvider provider.
In my provider, I did:
public class BasicRoleProvider : RoleProvider
{
[Inject]
private IAuthenticationService authenticationService;
/*Other stuff here*/
}
My Global.asax.cs file is as follows:
public class MvcApplication : NinjectHttpApplication
{
/* Other stuff here */
#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<IAuthenticationService>().To<AuthenticationService>();
this.Kernel.Inject(Roles.Provider);
}
}
#endregion
}
Whenever a method in the BasicRoleProvider gets executed and is using the authenticationService, its null. I think my problem lies in the Global.ascx.cs file. Am I doing the injection right?
It seems possible that you are using Ninject in an unsupported way.
From https://github.com/ninject/ninject/wiki/Changes-in-Ninject-2
Things that were in Ninject 1.x that are not in Ninject 2:
Field injection: This is a bad
practice, and has been cut for
minimization.
Because you tagged your question MVC 3, I assume you are linking to Ninject 2. As far as I know, Ninject 1 in an MVC 3 app would be a dead end.
The Inject attribute still exists, and fields must still be a valid target for it, which is why you do not get a compile time error.
But Ninject 2 will happily ignore that Inject attribute on the fields, which is why it is null for you.
I'M just trying to get started with Ninject 2 and ASP.NET MVC 2. I have followed this tutorial http://www.craftyfella.com/2010/02/creating-aspnet-mvc-2-controller.html to create a Controller Factory with Ninject and to bind a first abstract to a concrete implementation. Now I want to load a repository type from another assembly (where my concrete SQL Repositories are located) and I just cant get it to work. Here's my code:
Global.asax.cs
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
RegisterRoutes(RouteTable.Routes);
ControllerBuilder.Current.SetControllerFactory(new MyControllerFactory());
}
Controller Factory:
public class Kernelhelper
{
public static IKernel GetTheKernel()
{
IKernel kernel = new StandardKernel();
kernel.Load(System.Reflection.Assembly.Load("MyAssembly"));
return kernel;
}
}
public class MyControllerFactory : DefaultControllerFactory
{
private IKernel kernel = Kernelhelper.GetTheKernel();
protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
{
return controllerType == null ? null : (IController)kernel.Get(controllerType);
}
}
In "MyAssembly" there is a Module:
public class ExampleConfigModule : NinjectModule
{
public override void Load()
{
Bind<Domain.CommunityUserRepository>().To<SQLCommunityUserRepository>();
}
}
Now when I just slap in a MockRepository object in my entry point it works just fine, the controller, which needs the repository, works fine. The kernel.Load(System.Reflection.Assembly.Load("MyAssembly")); also does its job and registers the module but as soon as I call on the controller which needs the repository I get an ActivationException from Ninject:
No matching bindings are available, and the type is not self-bindable.
Activation path:
2) Injection of dependency CommunityUserRepository into parameter _rep of constructor of type AccountController
1) Request for AccountController
Can anyone give me a best practice example for binding types from external assemblies (which really is an important aspect of Dependency Injection)? Thank you!
Ok, I did some refactoring and now I got it running. I'll post the code of my Global.asax since that's where everything happens. I'm using the latest Ninject 2 Build with the latest Ninject.Web.Mvc Build for MVC 2.
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 = "Home", action = "Index", id = "" } // Parameter defaults
);
}
protected override void OnApplicationStarted()
{
AreaRegistration.RegisterAllAreas();
RegisterRoutes(RouteTable.Routes);
RegisterAllControllersIn(System.Reflection.Assembly.GetExecutingAssembly());
}
protected override Ninject.IKernel CreateKernel()
{
var kernel = new StandardKernel();
kernel.Load(new ExampleConfigModule());
return kernel;
}
}
public class ExampleConfigModule : Ninject.Modules.NinjectModule
{
public override void Load()
{
string connectionString =
ConfigurationManager.ConnectionStrings
["ConnectionString1"].ConnectionString;
string communityUserRepTypeName =
ConfigurationManager.AppSettings
["CommunityUserRepositoryType"];
var communityUserRepositoryType =
Type.GetType(communityUserRepTypeName, true);
Bind<Domain.CommunityUserRepository>().To(communityUserRepositoryType).WithConstructorArgument("conString",connectionString);
}
}
As you can see I got rid of my ControllerFactory, inherited from NinjectHttpApplication and load & bind the external assemblies' type in the Module. Now there might be a better way without specifiyng the type as a string in the config file, maybe you could declare the Module in the external assembly and let Ninject auto-load it from there but I still would need to pass the connection string down to the constructor of the concrete implementation. Maybe someone got an idea for this but for now this works fine.