I have an older application that has an ActionResult function that I would like to include in another newer application.
I have found that I can add the older controller as a reference, Instantiate the controller as an object, and then call the ActionResult function accordingly, like so:
public ActionResult test()
{
OlderApplication.Controllers.PatronController temp = new OlderApplication.Controllers.PatronController();
return temp.Index();
}
My problem lies in the fact that inside temp.Index() it references the Session, and it is coming up as null instead of having a value that should exist. Can I make session available this way?
In the example you've given you don't have two MVC applications - you have a single application that shares the same codebase. An "application" is defined by the webserver, but I digress.
The reason the Session within temp is null is because you aren't initializing the Controller correctly - Controllers are not POCOs, they require initialization. Call temp.Initialize(), however you'll need to create the RequestContext instance yourself, like so:
RequestContext context = new RequestContext( this.Context, this.RouteData );
OlderApplication.Controllers.PatronController oldController = new OlderApplication.Controllers.PatronController();
oldController.Initialize( context );
return oldController.Index();
Although if you're going through this step, you might as well just wire up your old controller into your Area Registration and/or URI Routing tables, thus negating the need to write this code.
As Dai pointed out you're not creating the controller correctly.
I would suggest using the controller factory to create your controller.
var oldController = ControllerBuilder.Current.GetControllerFactory().CreateController(Request.RequestContext, "Patron");
Related
I am developing an MVC app to serve multiple domains - each is a branch of a larger company.
A LocalBranch class stores details such as phone, address, email, location coordinates etc.
I want to create a single instance of this class per http request and have it available throughout the application - from within controllers, views, some helper classes and other code.
Is there a recommended way of doing this?
Right now I have it as a property on a BaseController and use ViewBagto pass it to views. But I would prefer it strongly typed in Views if possible.
I don't want to put it in an application variable, because we need to serve different values to different domains.
I would rather avoid a session variable if possible because we might scale up to use multiple servers in the future, and I've heard this doesn't play well with sessions.
Please feel free to update tags / title if you think there is a clearer way of expressing what I'm after. Thank you.
The best way to maintain your state in a web application per request is simply use the HttpContext class.
You need to store your state(LocalBranch) as an Item in the HttpContext:
HttpContext.Current.Items.Add("LocalBranch", GetLocalBranch());
You can fetch the Item all across your application like this:
LocalBranch branch = HttpContext.Current.Items["LocalBranch"] as LocalBranch;
The Items property is simply a key value Dictionary. The value is an object. You will have to check for nulls and this is really similar to the Session object you know. The main difference is the scope. The HttpContext is a dot net object that has a lifetime of an http request.
Now using the HttpContext the way I've shown you is the simplest way to do it.
You can go two steps forward and use a framework called Unity and add a lifetime to your objects.
Unity does much more and the lifetime management is just one gem.
You can create a custom HttpContext lifetime that generates objects per request. Something like this.
And them all you need to do is:
1.Register you LocalBranch class with the HttpContext lifetime.
2.Add a static Current property which will use the Unity container and resolve the correct instance of LocalBranch.
3.Use it something like this: LocalBranch.Current
BTW, you can use Unity's dependency injection for injecting objects into controllers and other modules. That's a better practice then just using the static Current property.
You kind of have two questions here. The first is "How do I create a single instance of this class per HttpRequest?" The second is "How do I make this available to strongly typed views?"
The first has pretty much been answered by #amir-popovich to use dependency injection. However, FWIW I would probably use Ninject instead of Unity (just preference, really) and I would probably implement it differently. I would not use HttpContext, and simply build a service (which is instanciated using Ninject's OnePerHttpRequest Module, passing the domain as an argument to get the proper values).
Then, in order to add these LocalBranch values to your strongly typed View Model, you can first create a base view model which holds this type:
public class BaseViewModel
{
public LocalBranch Branch {get;set;}
}
Then, make all of your current view models inherit this base type
public MyViewModel : BaseViewModel
{
public string SomeValue {get;set;}
}
Then in your controller, it is easy enough to add these values from the service you created from the first step
public ActionResult SomeAction()
{
var vm = new MyViewModel();
vm.Branch = LocalBranchService.GetLocalBranchValues(); //Local Branch Service has been injected with Ninject
//do other stuff
return View(vm);
}
However, that gets pretty tedious to add that to each controller action, so you can instead create a Result Filter to add it for you:
public class LocalBranchResultFilter : FilterAttribute, IResultFilter
{
public void OnResultExecuting(ResultExecutingContext filterContext)
{
//This method gets invoked before the ActionResult is executed.
filterContext.Controller.ViewData.Model.Branch = LocalBranchService.GetLocalBranchValues(); //Local Branch Service has been injected with Ninject
}
}
Now, you can just decorate your Controller and/or Actions with the filter (you could even set it in the Global Filters if you want).
You can embed the child actions into your layout or a view. You can even cache its output so you don't keep re-querying the database.
controller
[ChildActionOnly]
[OutputCache(Duration=500, VaryByParam="*")]
public ActionResult Info()
{
var localBranch = db.GetLocalBranch();
return PartialView("_Info", localBranch);
}
_Info view
This bit will get inserted into your other views
#model LocalBranch
<span>#Model.address</span>
<span>#Model.phone</span>
Use in _Layout or other view
<p>lorem ipsum...</p>
#Html.Action("Info")
Im working with a project that sets variables such as the current user profile object in its authorize action filter, storing them in the ViewData for access by the following action method.
The action method then calls functionality from the repository. I'm trying to find a way to access the ViewData from the repository WITHOUT modifying the repository's method signature, and am hoping there is a way I can track back to it via the HttpContext.Current functionality which I can call from the repository.
Can anyone help with this? Just to be clear, the only code that I can modify is within the repository method :(
public class MyController : Controller {
[MyAuthorize] // ViewData items are set here
public void MyAction(int id)
{
new MyRepository().DoSomething(id); // Need to access ViewData items within this repository method and am unable to alter the method signature :(
}
}
I'm pretty sure the answer is "no".
When you review the ASP.NET MVC source code, ControllerBase instantiates a ViewData dictionary on first use. Then when you call View(), a new ViewResult is instantiated with the ControllerBase.ViewData dictionary as a parameter. It does not look like it gets applied to a public static property or class like HttpContext which you could access from inside your repository.
I think your best bet would be to use HttpContext.Items which is built for this type of communication. Though probably not as ideal as just modifying the repository to accept the extra data.
I have an ASP.NET MVC 3 app, and have run into the following situation. On my page, I have a side bar, which can contain related links specific to that page, i.e., determined by controller type. The links will be determined by the current page's content.
I have followed Phil Haack's blog post on rendering dynamic side bars such as this using Html.Action and a separate controller. I like the separation of concerns this approach gives me: my controllers don't know anything about the side bar at all, which is the way it should be.
I now want to inject an instance of a derived type of SideBar into my SideBarController, an action on which will be called to render the side bar itself. There is one derived type of SideBar per controller, and so I find myself wanting to write code similar to this:
kernel.Bind<SideBar>().ToMethod(_ => controllerName == "foo"
? new FooSideBar(kernel.Get<UrlHelper>())
: new BarSideBar(kernel.Get<UrlHelper>()));
but there's quite a lot that is wrong about that fragment of code, not least the fact that I can't get hold of the controller name in the lambda, and the question of what happens when a third type of controller comes along, and then a fourth, etc.
Note that I can't use WhenInjectedInto<T>(), as the SideBar instance will always be injected into the SideBarController.
For what it's worth, the instances of SideBar are currently being created via the Ninject Factory extension, so the relevant side bar bindings are as follows (I've hard-bound an implementation of SideBar just to prove the approach so far works):
kernel.Bind<ISideBarFactory>().ToFactory().InRequestScope();
kernel.Bind<SideBar>().To<FooSideBar>().InRequestScope();
Finally, I essentially have a one-to-one mapping between the derived types of SideBar and the controller types. It feels a little bit like there might be a bit of duplication here, but also it represents the relationship between the components, so I think I'm ok with it.
This all makes me think that my approach to this part of the problem is wrong, and so I would welcome suggestions on how to achieve a clean solution with Ninject.
I'll have a go at answering, but I'm not near a computer at the moment, and so it'll be a bit vague.
Fundamentally, you can pass another parameter to Html.Action, so if that parameter is either the Request or something gleaned from the Request (such as the controller name or Url parts) then you can use that to determine which sidebar to show. You may need to inject the factory into the sidebar controller, and use it to create the correct controller, or some other fiddling about, but once you know which sidebar is required, it becomes much easier.
I am not sure if this is possible using ninject but it is using ModelBinding like so:
public interface ISidebar
{
}
public class Sidebar1 : ISidebar
{
}
public class Sidebar2 : ISidebar
{
}
public class SidebarModelBinder : IModelBinder
{
public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
var controller = controllerContext.RouteData.Values["Controller"];
var action = controllerContext.RouteData.Values["Action"];
switch (controller.ToString())
{
case "Home":
return new Sidebar1();
default:
return new Sidebar2();
}
throw new NotImplementedException();
}
}
public class TestController : Controller
{
public TestController()
{
}
public string Index(ISidebar sidebar)
{
//Do something with it
return "OK";
}
}
//Add to the Application_Start
ModelBinders.Binders.Add(typeof(ISidebar), new SidebarModelBinder());
EDIT: Took me a while, but managed to get it working using Ninject.
Please read it at: http://blog.voltje.be/2012/08/22/creating-a-dynamic-sidebar-with-asp-net-mvc-ninject/
Suggestion:
Don't inject sidebar.
Instead inject [sidebar]ContentProvider.
Bind a default implementation in global asax (per request), then unbind and rebind if needed in the controller.
I need to create something similar to how MVC invokes an Method(Action) and also uses the Model Binder to map a NamedValueCollection to the parameters on that method. Basically I have a Controller action that needs to dynamically call a method on a class, the controller has any information sent in a form or query string plus the name of the method to invoke as a string.
As far as I am concerned it's the same as invoking an action. I am essentially passing it down another level (there is a good reason I can't have these methods on the controller btw).
I downloaded the ASP.Net MVC 1.0 Source but there are a bunch of classes, I am having trouble finding the code that handles this.
I know how to invoke an method whose name is contained in a string, but maybe there is a better way that MVC uses. I also need to know how to use the Model Binders to make Request.Form + Query Strings to that methods parameters.
If anyone could point me to either the code in the MVC source that does this or point me in the right direction with regards to using the default Model Binder in MVC manually I would be grateful.
Let me know if I can make this clearer.
Thanks
It's complicated. FindAction is called on ControllerActionInvoker. This calls, eventually, ReflectedControllerDescriptor.FindAction, which in turn calls ActionMethodSelector.FindActionMethod, which calls RunSelectionFilters on the same type. That method takes a list of methods passed him by the collar, and iterates them, examining the arguments on each method and comparing them with the values in the request. Because this has to run quickly on a request comes in, this is all cached, and because it is designed to be extensible, there are some abstract types in between the layers I've described. Hence, it can be a little difficult to follow at first, and it would probably be difficult to repurpose it for non-controller logic. However, you could use it as a model for implementing your own system. I think it's a little too complicated for a domain-specific application. The number of extension points is probably appropriate for the MVC framework, but for your own code, YAGNI.
I hope this gives you enough to get started, however.
Regarding using a model binder without the web stack: Well, you still need MVC, but not necessarily a web server. Here's how we do it in a unit test:
internal static T Bind<T>(string prefix, FormCollection collection, ModelStateDictionary modelState) where T:BaseTimeRecordPresentationModel
{
var mbc = new ModelBindingContext()
{
ModelName = prefix,
ModelState = modelState,
ModelType = typeof(T),
ValueProvider = collection.ToValueProvider()
};
IModelBinder binder = new TimeRecordModelBinder();
var cc = new ControllerContext();
return binder.BindModel(cc, mbc) as T;
}
internal static T BindAndAssertValid<T>(string prefix, FormCollection collection) where T:BaseTimeRecordPresentationModel
{
var msd = new ModelStateDictionary();
var result = Bind<T>(prefix, collection, msd);
if (!msd.IsValid)
{
Assert.Fail(ModelStateValidationSummary(msd));
}
return result;
}
This may not be the correct way to use controllers, but I did notice this problem and hadn't figured out a way to correct it.
public JsonResult SomeControllerAction() {
//The current method has the HttpContext just fine
bool currentIsNotNull = (this.HttpContext == null); //which is false
//creating a new instance of another controller
SomeOtherController controller = new SomeOtherController();
bool isNull = (controller.HttpContext == null); // which is true
//The actual HttpContext is fine in both
bool notNull = (System.Web.HttpContext.Current == null); // which is false
}
I've noticed that the HttpContext on a Controller isn't the "actual" HttpContext that you would find in System.Web.HttpContext.Current.
Is there some way to manually populate the HttpContextBase on a Controller? Or a better way to create an instance of a Controller?
For now I'm going to do the following. This seems to be an acceptable fix...
public new HttpContextBase HttpContext {
get {
HttpContextWrapper context =
new HttpContextWrapper(System.Web.HttpContext.Current);
return (HttpContextBase)context;
}
}
Where this is added to a Controller class these Controllers are inheriting from.
I'm not sure if the HttpContext being null is the desired behavior, but this will fix it in the meantime for me.
Controllers are not designed to be created manually like you're doing. It sounds like what you really should be doing is putting whatever reusable logic you have into a helper class instead.
The HttpContext, in the ControllerContext is null because it is not set when the controller is created. The contructor of the controller does not assign this property, so it will be null. Normally, the HttpContext is set to the HttpContext of the ControllerBuilder class. Controllers are created by the ControllerBuilder class, followed by the DefaultControllerFactory. When you want to create your own instance of a controller, you can use the ExecuteMethod of the controller with your own ControllerContext. You don't want to do that is a real application. When you get some more experience with the framework you will find the appropriate method to do want you want. When you need ControllerContext in Unit test, you can use a mocking framework to mock the ControllerContext or you can class faking it.
You can find a model of the request flow in asp.net mvc on this blog.
When your new to Asp.net mvc, it's worth the effort to download the source code and read an trace the route how a request is processed.
Is it that you want to use some functionality from the controller? Or have the controller perform an action?
If it's the former, maybe that's some code that should be split out into another class. If it's the latter, you can do this to simply have that controller do a specific action:
return RedirectToAction("SomeAction", "SomeOtherController", new {param1 = "Something" });
Are you using a controller factory? If so, how are you registering components?
I ran into this problem where I had inadvertently added an HttpContext-based dependency as a Singleton, rather than Transient in Windsor.
HttpContext was null for all but the first request. It took me a while to track down that one.