I'm relatively new to ASP.NET MVC so I'm having a little trouble with this one. When the application start, I set a property to a certain value. Yet when I try to access this property in a controller (using this), the property is always null.
Is it something I am misunderstanding?
Here a simplified version of the code :
public class MvcApplication : System.Web.HttpApplication
{
internal ExtensionManager Extensions { get; private set; }
protected void Application_Start()
{
this.Extensions = new ExtensionManager();
}
}
public ActionResult ListExtension()
{
var app = HttpContext.ApplicationInstance as MvcApplication;
return View(app.Extensions.InstalledExtensions);
}
There are multiple HttpApplication instances created throughout the application lifecycle but Application_Start is only called once when the application first initialises.
See this for more info: http://msdn.microsoft.com/en-us/library/ms178473%28VS.80%29.aspx
I don't know what you are trying to store here (extensions?) but if its global to all users then you could use a static property, otherwise store it somewhere else.
Late answer (but for anyone in need of assistance).
I had this issue as well.
I think you can use the Application["myKey"] array, to set some values. Sure they aren't instance properties, but you can set a dependency injection container (like unity, who recommends this option in their code sample) , then access it from your controller with Application["myKey"].
You should set only static data during application start. Do not set
any instance data because it will be available only to the first
instance of the HttpApplication class that is created.
Related
I have an ASP.NET MVC application using StructureMap.
I have created a service called SecurityContext which has a static Current property. A simplified version looks like this:
public class SecurityContext : ISecurityContext
{
public bool MyProperty { get; private set; }
public static SecurityContext Current
{
get
{
return new SecurityContext() { MyProperty = true };
}
}
}
I've hooked this up in my StructureMap registry as follows:
For<ISecurityContext>().Use(() => SecurityContext.Current);
My understanding of this Linq expression overload of the Use method is that the returned concrete object is the same for the entire HTTP request scope.
However, I've set up a test case where my context interface is injected in two places, once in the controller's constructor and again using the SetterProperty attribute in the base class my view inherits from.
When debugging I observe the Current static method being hit twice so clearly my assumptions are wrong. Can anyone correct what I'm doing here? The reason I want this request-scoped is because I'm loading certain data into my context class from the database so I don't want this to happen multiple times for a given page load.
Thanks in advance.
The default lifecycle for a configuration is Transient, thus each request for an ISecurityContext will create a new instance of SecurityContext. What I think you want is to use the legacy HttpContext lifecycle.
Include the StructureMap.Web nuget package. Then change your configuration to the following:
For<ISecurityContext>()
.Use(() => SecurityContext.Current)
.LifeCycleIs<HttpContextLifecycle>();
More information on lifecyles can be found here.
The HttpContextLifecycle is obsolete, however I do not know if or when it will be removed. The StructureMap team does recommend against using this older ASP.Net lifecycle. They state in the documentation that most modern web frameworks use a nested container per request to accomplish the same scoping. Information about nested containers can be found here.
I don't know if the version of ASP.Net MVC you are using is considered a modern web framework. I doubt it is because ASP.Net Core 1.0 is the really the first in the ASP.Net line to fully embrace the use of DI. However, I will defer to #jeremydmiller on this one.
There seems to be some weird behavior in ASP.NET Web API (4.0.30506) that I haven't witnessed before.
What I'm seeing is that the same action filter attribute instance is reused over Web API requests. This is especially a problem in case this attribute gets dependencies injected to it, since those dependencies might be specific to the web request. I'm aware that it's better for attributes to be passive, but my assumption was that action filter attributes where not cached.
I searched for any articles, blog posts or Microsoft change logs that described this and the reason behind this, but I couldn't find a single thing. That makes me wonder whether there is something wrong with my configuration that makes this happening. Thing is however, that I'm able to reproduce this issue in a new and empty Visual Studio 2012 Web API project.
What I did was create a new empty project using the Visual Studio 2012 ASP.NET MVC 4 Web Application project with the "Web API" template. It comes with the Web API 4.0.20710.0 NuGet package. After that I added the following attribute:
[DebuggerDisplay("{id}")]
public class TestFilterAttribute : System.Web.Http.Filters.ActionFilterAttribute {
private static readonly List<int> used = new List<int>();
private static int counter;
private readonly int id;
public TestFilterAttribute() {
this.id = Interlocked.Increment(ref counter);
}
public override void OnActionExecuting(HttpActionContext actionContext) {
// Just for testing: I would expect this line never to throw, but it does.
if (used.Contains(this.id)) throw new Exception("WAT?");
used.Add(this.id);
base.OnActionExecuting(actionContext);
}
}
And I add this attribute to the ValuesController (part of the default template):
public class ValuesController : ApiController {
// GET api/values
[TestFilterAttribute]
public IEnumerable<string> Get() {
return new string[] { "value1", "value2" };
}
// ...
}
Now when I start the project, go to the /api/values in the browser and refresh that page a few times, the "WAT?" exception is thrown.
Is this normal behavior of Web API and if so, what's the reasoning about this? Or Did I miss some memo about this change somewhere? Does this make Web API attributes extra unsuited for doing dependency injection? Or am I'm doing something wrong?
Web API is built on top of MVC, thus it uses a lot of it's functionality.
Attribute instance re-usability is part of the aggressive caching introduced by MVC 3. This means that the same Attribute instance will most likely be used with all the Actions it is applied on. MVC pipeline will do it's best at trying to treat your Attribute class like a Singleton.
Because the same Attribute instance is reused, it's Constructor is not called and id is not incremented. If, for example, you increment id inside OnActionExecuting, all will work well.
You can still do everything you want with your Attribute. You only need to keep in mind that you are not guaranteed to always get a new instance created. The constructor shouldn't contain anything but initial initialization.
public TestFilterAttribute() {
// Instance will be reused thus this will not be called for each Action
}
public override void OnActionExecuting(HttpActionContext actionContext) {
// Called on each Action
}
I started with a WebForms .NET 4.51 application. I then added WebAPI to the same application. In Session_Start() I create a variable instance that I store within the session as follows:
public class Global : HttpApplication
{
protected void Session_Start(object aSender, EventArgs aEventArgs)
{
//Create an object to hold all the settings for the user in the session. This is only loaded once we
//have a user successfully logged in
HttpContext.Current.Session[SYSTEM_SETTINGS_SESSION_KEY] = new SystemSettings();
}
}
and I have a simple property accessor as follows:
public class Global : HttpApplication
{
public static SystemSettings SystemSettings
{
get
{
if (HttpContext.Current == null)
return null;
return HttpContext.Current.Session[SYSTEM_SETTINGS_SESSION_KEY] as SystemSettings;
}
}
}
The above all works well when I am accessing the property from code except when I attempt to do this from within a WebAPI controller as follows via the property from above viz Global.SystemSettings:
public class EmailActivitiesController : ApiController
{
emailBody = EmailToClientsTemplateBuilderHelper.TemplateContentBuild(emailBody, Global.SystemSettings);
}
When I inspect HttpContext.Current.Session it is NULL.
So why is the Session collection null when accessed from the WebAPI controller?
I need to store use the information related to the user's session in the WebAPI controller, so do I need to store things differently now?
UPDATE
The accepted solution also worked for WebAPI 1 which is what the application is using.
This is because WebApi does not have Session enabled by default. WebApi is trying to encourage you to move towards stateless HTTP and RESTful APIs.
I strongly urge you to rework your design without sessions.
With this said, you can enable Session in WebApi 2 by adding this to Global.asax
protected void Application_PostAuthorizeRequest()
{
System.Web.HttpContext.Current.SetSessionStateBehavior(
System.Web.SessionState.SessionStateBehavior.Required);
}
I have created a custom membership provider that takes an instance of IUsersRepository in it's constructor.
private IUsersRepository usersRepository;
public CustomMembershipProvider(IUsersRepository usersRepository)
{
this.usersRepository = usersRepository;
}
This dependency is bound using Ninject
Bind<IUsersRepository>().To<SqlUsersRepository>().WithConstructorArgument("connectionString", ConfigurationManager.ConnectionStrings["AppDb"].ConnectionString);
Bind<MembershipProvider>().To<CustomMembershipProvider>();
and used in my AccountController like so
CustomMembershipProvider provider;
public AccountController(MembershipProvider membershipProvider)
{
this.provider = (CustomMembershipProvider)membershipProvider;
}
[HttpPost]
public ActionResult Register(User user)
{
MembershipCreateStatus status = new MembershipCreateStatus();
provider.CreateUser(user.FirstName, user.LastName, user.Email, user.Password, out status);
return View(user);
}
The problem with this is that when CustomMembershipProvider is instantiated the Initialize method is not called and thus my modified Web.Config is not read.
As a side to this, I've noticed that CustomMembershipProvider is being instantiated twice - the first time as I explained above, and then again when my [HttpPost] action method is called. The second time it's instantiated using a parameterless constructor and it calls the Initialize method. I don't know what happens to the second CustomMembershipProvider as provider.CreateUser() uses my un-Initialized CustomMembershipProvider.
I hope I've explained this well enough, any help would be appreciated.
I can't tell if you're using the Ninject.Mvc3 extension (which you probably should), but that will allow you to have a single instance of your MembershipProvider per web request. You'll have to do the binding like so:
Bind<MembershipProvider>().To<CustomMembershipProvider>().InRequestScope();
If you want to return the same instance every time you can use InSingletonScope.
Accessing web.config is not possible at the time bindings tend to be done in Mvc apps, but I usually get around that by having a custom configuration section and binding that to a method. By doing that the method will not get evaluated until the kernel is asked for a configuration section, and at that time web.config can be accessed. Something similar might work for your connection string.
Bind<MyConfigurationSection>().ToMethod(context => (MyConfigurationSection)ConfigurationManager.GetSection("mysection")).InSingletonScope();
Public Class SomeRolProvider
Inherits RoleProvider
Implements IProvider
'this service needs to get initialized
<Inject()>
Public Property _memberhip As IMemberschipService
Sub New()
'only this constructor is called
End Sub
Protected Overrides Function CreateKernel() As Ninject.IKernel
Dim modules = New NinjectModule() {New Anipmodule()}
Dim kernel = New StandardKernel(modules)
kernel.Inject(Roles.Provider)
kernel.Inject(Membership.Provider)
Return kernel
End Function
This will force the kernel to bind properties of the memberschip provider
Thanks for everybody's help on this question. I was unable to find a solution that would work well for this applications situation without the need for masses of code.
To get around the issue I looked at the default MVC 2 project and copied some of Microsoft's code. My project is probably not very testable but I needed a quick solution. I've set it up so that if I do have time to find a solution in the future, I'll be able to replace it with the current code.
I have been searching for a while.
I'm not new to dependency injection and have used StructureMap with several projects MVC and the like, but I felt like giving Ninject a go, so as not to miss out on the fun.
I am trying to use Ninject with an existing web app which I am bringing up-to-date.
I couldn't find on the blogs and wiki provided by Ninject, I am a little impatient to be honest so may have missed it, and the first few pages of google appear to be out of date or talking about using MVC with Ninject.
So far I have the following and it works, but I was hoping someone could point out a less intrusive option, regarding calling the ServiceModule to the Kernel and injecting a property with the desired bind from the web app.
What I have so far is a ServiceModule:
public class ServiceModule : NinjectModule
{
public override void Load()
{
string connectionString = ConfigurationManager.ConnectionStrings["ConnectionString"].ConnectionString;
Bind<IPreRegistrationService>().To<PreRegistrationService>()
.WithConstructorArgument("connectionString",connectionString);
}
}
Then in my page I have defined a private variable:
private IPreRegistrationService xfemPreRegistrationService = null;
Then in the page load event:
IKernel kernel = new StandardKernel(new ServiceModule());
xfemPreRegistrationService = kernel.Get<IPreRegistrationService>();
So this works, but what I would like is to move on to a phase where all I define is:
[Inject]
public IPreRegistrationService xfemPreRegistrationService { get; set; }
on a page and the rest is magic.
Cheers
Thanks to this stackoverflow post I found out about the extension Ninject.Web
Problem I found was you need to start off using Ninject.Web and I could not as I already have a PageBase defined to handle securities and the such.
So, the only way I could see was to take the KernelContainer class from the project (as KernelContainer is defined as internal):
Then call from the global asax OnApplicationStart:
KernelContainer.Kernel = new StandardKernel(new ServiceModule());
// Request injections for the application itself.
KernelContainer.Inject(this);
Then in my PageBase from the OnInit method:
KernelContainer.Inject(this);
This has allowed me to reach my target of simply putting:
[Inject]
public IPreRegistrationService xfemPreRegistrationService { get; set; }
where needed