ASP.NET MVC - Set ViewData for masterpage in base controller - asp.net-mvc

I'm using a masterpage in my ASP.NET MVC project. This masterpage expects some ViewData to be present, which displays this on every page.
If I don't set this ViewData key in my controllers, I get an error that it can't find it. However, I don't want to set the ViewData in every controller (I don't want to say ViewData["foo"] = GetFoo(); in every controller).
So, I was thinking of setting this in a base controller, and have every controller inherit from this base controller. In the base controller default constructur, I set the ViewData. I found a similar approach here: http://www.asp.net/learn/MVC/tutorial-13-cs.aspx. So far so good, this works... but the problem is that this data comes from a database somewhere.
Now when I want to Unit Test my controllers, the ones that inherit from the base controller call its default constructor. In the default constructor, I initialize my repository class to get this data from the database. Result: my unit tests fail, since it can't access the data (and I certainly don't want them to access this data).
I also don't want to pass the correct Repository (or DataContext, whatever you name it) class to every controller which in turn pass it to the default controller, which I could then mock with my unit tests. The controllers in turn rely on other repository classes, and I would end up passing multiple parameters to the constructor. Too much work for my feeling, or am I wrong? Is there another solution?
I've tried using StructureMap but in the end I didn't feel like that is going to fix my problem, since every controller will still have to call the base constructor which will initialize the repository class, so I can't mock it.
This is a similar question but I find no satisfactory answer was given. Can I solve this in a neat way, maybe using StructureMap as a solution? Or should I jsut suck it and pass a Repository to every controller and pass it again to the base controller? Again, It feels like so much work for something so simple. Thanks!

I see two options:
First:
Set the ViewData for MasterPage in YourBaseController.OnActionExecuting() or YourBaseController.OnActionExecuted():
public class YourBaseController : Controller
{
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
// Optional: Work only for GET request
if (filterContext.RequestContext.HttpContext.Request.RequestType != "GET")
return;
// Optional: Do not work with AjaxRequests
if (filterContext.RequestContext.HttpContext.Request.IsAjaxRequest())
return;
...
filterContext.Controller.ViewData["foo"] = ...
}
}
Second:
Or create custom filter:
public class DataForMasterPageAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
// Optional: Work only for GET request
if (filterContext.RequestContext.HttpContext.Request.RequestType != "GET")
return;
// Optional: Do not work with AjaxRequests
if (filterContext.RequestContext.HttpContext.Request.IsAjaxRequest())
return;
...
filterContext.Controller.ViewData["foo"] = ...
}
}
and then apply to your controllers:
[DataForMasterPage]
public class YourController : YourBaseController
{
...
}
I think the second solution is exactly for your case.

Related

Dependency Injection into an MVC action method

I'm wondering if this is possible. I have a typical MVC action method with a signature that looks like this:
public ActionResult View(MyModel model)
{
IAnObject myObject = new AnObject();
//several lines of code follow.....
return View(model);
}
I'd like to get rid of that new keyword and inject an instance of IAnObject into the action method. But I'm not sure if MVC allows for this, injecting a class along side a model in an action method? Has anyone run across this, and are there ways of tackling it? (Our IoC container is Windsor, in case that makes a difference.)
If you are expecting to inject this reference into the action method as a parameter, you can look to the ControllerActionInvoker, which has an InvokeActionMethod method, which I believe is called from InvokeAction. This method has a list of parameters passed into it, and a description of the action (ActionDescriptor class). This action descriptor has a GetParameters method that will give you more detailed information about the parameter, such as type information that you would need for the dependency injector. I've not done this, so I don't know quite how it works out, but it seems possible.
I also don't know how that might affect how MVC selects an action method to post to, so factor that in.
You may want to do your injection in OnActionExecuting which is called before any action on the controller is executed. This will give you context such as the Request but will allow you to set member variables - thus 'simulating' constructor injection. And of course you only have to do it once for the whole controller.
[NonAction]
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
_myService = .........; // get from IoC container
base.OnActionExecuting(filterContext);
}
Well, I agree with the guys on the comments, but if you want to take an instance in the method scope, try to get it from your container of IoC, something like this:
public ActionResult View(MyModel model)
{
// take from the container of IoC
IAnObject myObject = _continerIoC.Resolve<IAnObject >();
//several lines of code follow.....
return View(model);
}
Avoid using the new to create your instance and your concrete type in the container and decouple your controller from dependecies/references.
I really consider using constructor/property Injection. There is a method injection too.

How can I create a binding in Ninject that changes based on the requested controller?

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.

MVC + Multiple tenancy app

So I have an MVC app that should change the Website title, and header color based on the domain the app is hit from. So I have a simple table setup in SQL as such:
DomainName (PK), WebsiteTitle, HeaderColor
Domain1.com, Website Title for Domain 1, #ebebeb
So I am trying to figure out the best way to return this information for each page view. Sure I can go ahead and lookup the site info in each model thats returned from the controller. But are there any other ways I can approach this? Maybe at a lower level in the stack?
Thank you!
There are many ways you can do this. ActionFilters are one way, or in a BaseController.
You need to determine if every action requires this, or if only certain actions.
If you decide every action, create a controller base, inheriting from Controller, then overriding OnActionExecuting. In that method you can make you calls to fetch and add the data to viewdata. Like so:
public class BaseController : Controller
{
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
filterContext.Controller.ViewData.Add("SiteTitle", "Site title");
base.OnActionExecuting(filterContext);
}
}
If you prefer to use a base viewmodel that has this information, it would be best to override OnActionExectued where you can get access to the actions results, and modify the base model to set your values. Like so:
public class BaseController : Controller
{
protected override void OnActionExecuted(ActionExecutedContext filterContext)
{
var result = filterContext.Result as ViewResultBase;
var baseModel = (BaseViewModel) result.ViewData.Model;
baseModel.SiteTitle = "Site Title";
base.OnActionExecuted(filterContext);
}
}
Depending if you want an inheritence chain for your viewmodels. Either works. You'll also notice that I just set the values. Use whatever source for values you need. If you are pulling them from the db, I would cache the values so that for every action you are not hitting the db for it.
This problem is fundamentally identical to swapping layout or master pages for mobile vs desktop browsers. However, instead of looking at the device caps in a web request to determine which layout to use, you'd check the domain of the request.
See this article for a slightly complex (but thorough) overview of selecting mobile vs desktop views. Much of what the author says is focused on detecting screen solution, etc., which doesn't directly apply to you, but the mechanism for selecting the master or layout page should be just what you're looking for.
Or, you can handle this through inheritance.
Implement a base controller, like so:
public class BaseController : Controller
{
public string SiteTitle { get { .... } }
public string HeaderColor { get { ... } }
/// whatever other "global" properties you need
}
Then, each of your controllers inherit from BaseController
public class HomeController : BaseController
{
public ActionResult Index()
{
var myTitle = SiteTitle;
/// then, do whatever you want with it
return View();
}
}
In the property accessors in BaseController, read the title and whatever other properties you need from a .settings file or the AppSettings section in web.config.
Controller also provides events that can be used to set these properties so that you don't have to duplicate any code for getting those values into each view.

asp.net mvc - how to enforce controller layer supertype

I'd like to use a class extending Controller as the default base type for controllers in my project, as opposed to using Controller itself. So, I would be doing this:
public class FooController : MyBaseController
Is there a way I can enforce this, so that people cannot create controllers which extend Controller directly?
You can always create a unit test that (via reflection) retrieves all classes that derive from Controller and asserts that each class is also a subclass of MyBaseController. It would be along the lines of
[TestMethod]
public class All_Controllers_Derive_From_MyBaseController()
{
// Act
var controllerTypes = AppDomain.CurrentDomain
.GetAssemblies()
.SelectMany(asm => asm.GetTypes())
.Where(t => t.IsSubclassOf(typeof(Controller))
.ToList();
// Verify
foreach (var type in controllerTypes)
{
// Make sure the type isn't the actual controller type
if (type is Controller)
continue;
Assert.IsTrue(type.IsSubclassOf(typeof(MyBaseController)),
string.Format("{0} is not a subclass of the MyBaseController class", type.FullName));
}
}
Now if someone creates a controller that doesn't use your base controller your unit tests will fail and tell you which ones are not correct.
Note that this code was written free-hand, so it might need some adjustment but that's the basic idea.
However I prefer the unit testing approach above here is another one by using a custom controller factory.
public class MyControllerFactory<T> : DefaultControllerFactory where T : Controller
{
#region Overrides of DefaultControllerFactory
protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
{
if (!typeof(T).IsAssignableFrom(controllerType))
{
throw new NotSupportedException();
}
return base.GetControllerInstance(requestContext, controllerType);
}
#endregion
}
You can set it up in the application start method of your Global.asax like this:
ControllerBuilder.Current.SetControllerFactory(new MyControllerFactory<MyBaseController>());
This of course causes a runtime exception when not deriving from MyBaseController which may not be suitable in your current scenario.
You have two choices and both involve writing some code.
choice #1 You can create a filter and try and catch the error at runtime. You better have good a good user acceptance test process in place to touch all of the pages.
Choice #2 involves writing a task for MSBuild that checks that each controller class is derived from your specified class. Just load the application assembly (or assemblies) and go to town!
I prefer choice #2. It doesn't affect application runtime performance and gives you better coverage. You can run it at the end of a build.
http://msdn.microsoft.com/en-us/library/gg416513(VS.98).aspx
http://msdn.microsoft.com/en-us/library/t9883dzc.aspx
I want to question your reasons, but here's an answer: One (slightly underhanded) method you could use is to name the base class "Controller" and put it in the same namespace as your individual controllers. As long as people don't specifically extend System.Web.Mvc.Controller, they'll get your Controller class. None the wiser, possibly.
You could modify the default code generation template, so that it always creates a new controller that inherits from the correct class. The downside is this involves distributing the updated templates to the dev team. See Scott Hanselman's blog for more detailed instructions.
http://www.hanselman.com/blog/ModifyingTheDefaultCodeGenerationscaffoldingTemplatesInASPNETMVC.aspx

ASP.NET StrongTyped Controller-Action View<TView,TModel>(TModel Data)

I'm using Asp.net MVC 1 and I would really like my controller actions to use StronglyTyped View(data) calls that enforce type checking at compile time and still let me use aspx pages under the default view engine. The ViewPages I call are strongly typed, but errors in the action's call to View(data) can't be caught at compile time because the built in Controller View(data) method isn't strongly typed and doesn't even check to see if the page exists at compile time.
I've implemented a partial solution (code below) using this post but (1) I can't get the generic View function to recognize the Type of strong view pages unless I create a code behind for the strongly typed view, and (2) Intellisense and refactoring don't work properly with this method which makes me doubt the reliability of the method I'm using.
Question:
Is there a better way to get type enforcement when calling Views from actions?
Alternative: Is there an alternative method where my action method can create an instance of a viewpage, set some properties directly and then render out its HTML to the action response?
Code:
Here's the base Class all my Controllers Inherit from to achieve what I have so far:
public class StrongController : Controller
{
protected ActionResult View<TView, TModel>(TModel model)
where TView : ViewPage<TModel>
where TModel : class
{
return View(typeof(TView).Name, model);
}
}
And here's an example Controller in use:
namespace ExampleMVCApp.Controllers
{
public class HomeController : StrongController
{
public ActionResult Index()
{
return View<ExampleMVCApp.Views.Home.Index, ExampleData>(new ExampleData());
}
}
}
ViewPage Code Behind Required for Type Recognition... Aspx header didn't work
namespace ExampleMVCApp.Views.Home
{
public class Issue : System.Web.Mvc.ViewPage<ExampleData>
{
}
}
I think you should give the T4MVC helpers a spin (one of the original announcements here). This would at least enable you to get rid of the code you already have, since these templates generate the code based on the Views you already have and you employ these "fake" method calls to address your views.
For having your calls to View to be strongly typed for the specific model declared by your view, I am not exactly sure if these helpers help you with that (though I suspect they do). However, if they don't you can still hack the T4MVC code to do so yourself or get in touch with the original author, David Ebbo, to suggest the feature for addition.

Resources