In asp.net MVC, dependency injection with controllers is simple and straightforward. Now, I'd like to remove most of the logic from views by using helpers. The problem is that these helpers use some of the objects that are injected.
Let me write an example:
public interface ISessionData
{
List<string> IdList {get;}
}
public MyController : Controller
{
public MyController(ISessionData sessionData)
{
...
}
}
session data is injected into controller. So far so good. But now I have a helper. Let's say it looks like this:
public class MyHelper
{
private readonly ISessionData sessionData;
public MyHelper(ISessionData sessionData)
{
this.sessionData = sessionData;
}
public bool CheckSomethingExistsInSession(string id)
{
return sessionData.IdList.Any(x => x.Id.Equals(id));
}
}
Now what? I'd like MyHelper to be injected into view. Only way I can see is adding this helper to model and passing it to view every time. Any other ideas?
In MVC it is better to pass ISessionData data from Controller to View (using ViewModel or ViewData):
ViewData["Session"] = sessionData.IdList.ToList();
And remove ISessionData dependency from the helper. Something like this:
public class MyHelper
{
//private readonly ISessionData sessionData;
public MyHelper(/*ISessionData sessionData*/)
{
//this.sessionData = sessionData;
}
public bool CheckSomethingExistsInSession(string id, IList<...> sessionData)
{
return sessionData.Any(x => x.Id.Equals(id));
}
}
In View:
<% var somethingExists = new MyHelper().CheckSomethingExistsInSession(
1, ViewData["Session"] as IList<...>); %>
UPDATED:
public static class MyHelper
{
public static bool CheckSomethingExistsInSession(string id, IList<...> sessionData)
{
return sessionData.Any(x => x.Id.Equals(id));
}
}
<% var somethingExists = MyHelper.CheckSomethingExistsInSession(
1, ViewData["Session"] as IList<...>); %>
You should remove session logic from your controller's constructor and insert it into the controllers action method by using an IModelBinder. See below:
public class SessionDataModelBinder : IModelBinder
{
public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
// Get/create session data implementating ISeesionData or whatever here. This will be return to the controller action method.
return new SessionData()
}
}
On you controller you would do something like:
public MyController : Controller
{
public MyController()
{
....
}
public ActionResult Index(ISessionData sessionData)
{
// do stuff with ISessionData.
// Redirect or whatever.
return this.RedirectToAction("Index");
}
}
You need to add your IModelBinder like below for it to be called. You can do this in the http application's startup.
System.Web.Mvc.ModelBinders.Binders[typeof(ISessionData)] = new SessionDataModelBinder();
Related
I have a block of code which is use in pretty much every controller, so I am wondering how, or what's the best practice for reusing code in multiple controllers
Simple example would be this
public String CoolCode(){
// Stuff
return MyStuff;
}
Then in another controller I just use
string something = CoolCode();
Where should I put it, and how to use it in every controller?
Personally I would inject the helper class into the controller:
public interface IHelper
{
string CoolCode();
}
public class Helper : IHelper
{
public string CoolCode()
{
return "Cool code";
}
}
public class SomeController
{
private IHelper _helper;
public SomeController(IHelper helper)
{
_helper = helper;
}
public ActionResult Index()
{
//call _helper.CoolCode();
}
}
Then you would need to inject this using some sort of IoC container, I recommend Castle Windsor
This is all quite abstract but I recommend you read up on it:
http://www.codeproject.com/Articles/560798/ASP-NET-MVC-Controller-Dependency-Injection-for-Be
What you should do is extend the Controller class that all your controllers inherit now and use this abstraction to wrap all of your controllers that use that code:
public class MyControllerBase : Controller
{
public string CoolCode() { ... }
}
now you simply inherit your abstraction rather than the default one:
public class AnyController : MyControllerBase
{
...
}
However depending on what you need precisely different approaches might be more appropriate.
public interface IBaseUserController
{
string SomePropety { get; set; }
ActionResult SignUp(string code, [Form] SomeViewModel model);
}
public class BaseUserController : Controller, IBaseUserController
{
private static string _somePropety = "";
public BaseUserController(){}
public string SomePropety
{
get
{
return _somePropety;
}
set { _somePropety = value; }
}
public virtual ActionResult SignUp(string code, [Form] SomeViewModel model)
{
// ... CoolCode maybe use SomePropety
return View(model);
}
}
public class TestUserController : BaseUserController
{
public TestUserController()
{
SomePropety = "Value";
}
public override ActionResult SignUp(string code, [Form] SomeViewModel model)
{
return base.SignUp(code, model);
}
public ActionResult SignUp2(string code, [Form] SomeViewModel model)
{
return base.SignUp(code, model);
}
}
MVC controller are same as normal class and controller have the same extension .cs
So use can use static method like following.
HomeController objHomeController = new HomeController();
string something= objHomeController.CoolCode();
I am new to mvc and I load ViewBag in a method of controller as,
HomeController: Controller
{
Public ActionResult Index()
{
loadViewBag();
return View();
}
public void loadViewBag()
{
ViewBag.aaa = "something";
}
}
It works fine.
What is my problem is, Now I want to call loadViewBag() method form another controller( say Account) so that I can reuse same method and need to make loadViewBag() method static due to some static variables as:
public static void loadViewBag()
If I make loadViewBag method static, there appear error on ViewBag " An object reference is required for the non-static field, method, or property 'System.Web.Mvc.ControllerBase.ViewBag.get' ".
Is there any solution/suggestion.
Thank You.
Just make it an extension method of ControllerBase e.g.
public static void ControllerExt
{
public static void LoadViewBag(this ControllerBase controller)
{
controller.ViewBag.aaa = "something";
...
}
}
That way you can use it in any controller
public class HomeController : Controller
{
public ActionResult Index()
{
this.LoadViewBag();
return View();
}
}
public class AccountController : Controller
{
public ActionResult Index()
{
this.LoadViewBag();
return View();
}
}
If its only specific to some controllers then it would be more flexible to pass the ViewBag property in e.g.
public static class ControllerHelper
{
public static void LoadViewBag(dynamic viewBag)
{
viewBag.aaa = "something";
}
}
public class HomeController : Controller
{
public ActionResult Index()
{
ControllerHelper.LoadViewBag(ViewBag);
return View();
}
}
ViewBag is a property of your controller (more specifically of ControllerBase), and since a static method has no knowledge of a class instance, you can't access it.
You could pass the controller instance to the method if you want to use a static method or even make it an extension method, but depending on your problem, this solution could be sub-optimal. You may be able to get a better answer if you add more details to your question.
Public ActionResult Index()
{
this.loadViewBag();
return View();
}
public static void loadViewBag(this ControllerBase target)
{
target.ViewBag.aaa = "something";
}
Do you need that to allow different controllers/views to use some common properties?
Then I'd rather recommend a common base controller, while also wrapping ViewBag code into type safe properties (to let the compiler control the data consistency - as you know, ViewBag is not type safe, so any typos and data mismatches won't be noticed until the code gets executed).
1. Introduce a common controller with those wrapper properties
public abstract class MyBaseController : Controller
{
internal long CurrentUserId
{
get { return ViewBag.CurrentUserId; }
set { ViewBag.CurrentUserId = value; }
}
internal Role CurrentUserRole
{
get { return ViewBag.CurrentUserRole; }
set { ViewBag.CurrentUserRole = value; }
}
...
}
Thus, your inherited controllers could simply set the properties - or, with lots of common code just introduce a method in your base controller - similar to what you already have.
2. Introduce a common view class with those wrapper properties
public abstract class MyBaseViewPage<T> : WebViewPage<T>
{
public string Title
{
get { return (string)ViewBag.Title; }
set { ViewBag.Title = value; }
}
public long CurrentUserId
{
get { return (long)ViewBag.CurrentUserId; }
}
public Role CurrentUserRole
{
get { return ViewBag.CurrentUserRole; }
}
}
public abstract class MyBaseViewPage : MyBaseViewPage<dynamic>
{
}
and update web.config to let MVC know you're using a custom base view:
<configuration>
...
<system.web.webPages.razor>
...
<pages pageBaseType="MyRootNamespace.Views.MyBaseViewPage">
...
</pages>
</system.web.webPages.razor>
Now you can use them as normal properties in your controllers and views.
I am using MVC 2.
I have a BaseController class that every Controller uses. In this base controller class there is a property called IsAdministrator. I need to use this method in my view's HTML part. How would I do this?
EDIT:
My property in my BaseController is defined like this:
public bool IsAdministratorUser
{
get { return ... }
}
One way would be to use an HTML helper:
public static class HtmlExtensions
{
public static bool IsAdministrator(this HtmlHelper htmlHelper)
{
var controller = htmlHelper.ViewContext.Controller as BaseController;
if (controller == null)
{
throw new Exception("The controller used to render this view doesn't inherit from BaseContller");
}
return controller.IsAdministrator;
}
}
And in your view:
<% if (Html.IsAdministrator()) { %>
<% } %>
UPDATE:
#jfar's comment about the MVC paradigm is correct. Here's what you could do in practice to implement it. You could define a base view model class that all your view models derive from:
public class BaseViewModel
{
public bool IsAdministrator { get; set; }
}
and then write a custom action filter attribute which will execute after the action and set the property:
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class AdministratorInjectorAttribute : ActionFilterAttribute
{
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
base.OnActionExecuted(filterContext);
var result = filterContext.Result as ViewResultBase;
if (result != null)
{
// the action returned a strongly typed view and passed a model
var model = result.ViewData.Model as BaseViewModel;
if (model != null)
{
// the model derived from BaseViewModel
var controller = filterContext.Controller as BaseController;
if (controller != null)
{
// The controller that executed this action derived
// from BaseController and posses the IsAdministrator property
// which is used to set the view model property
model.IsAdministrator = controller.IsAdministrator;
}
}
}
}
}
And the last part is to decorate the BaseController with this attribute:
[AdministratorInjector]
public abstract class BaseController : Controller
{
public bool IsAdministrator { get; set; }
}
Finally if your view is strongly typed to a model that derives from BaseViewModel you could directly use the IsAdministrator property:
<% if (Model.IsAdministrator) { %>
<% } %>
Probably a bit more code than the HTML helper, but your consciousness about respecting MVC paradigm will be clear.
I'm using Ninject to do some dependancy injection. (Mainly for the DAL), my project consists of 3 aspects and are as follows,
Project.Lib (Everything database,
services and anythign else that is
logic)
Project.Admin (Administration)
Project.Web (Front end what the user
see's)
Now, each of my controllers within my projects inherit from a BaseController
public abstract class BaseController : Controller
{
protected BaseController(ISession session)
{
_Session = session;
}
public ISession _Session { get; private set; }
}
And then and example controller might be like so,
public class ImageController : BaseController
{
private MediaService _mediaService;
public ImageController(ISession session) : base(session)
{
_mediaService = new MediaService(session);
}
[HttpGet]
public ActionResult List()
{
var RetVal = _mediaService.GetAllImages();
return View(RetVal);
}
}
As you can see the "Session" is passed from the controller to the service layer. I'm curious as to if this is good practie? ANy negitives to what we are doing here?
I'd avoid referencing ISession through your controller. A better solution would be to use Ninject to inject your services into your controllers. In this instance you'll need to create an abstraction for your MediaService class e.g.:
public interface IMediaService
{
SomeCollection GetAllImages();
// ...
}
You'd then use Ninject to supply an implementation of the above interface to your controller:
public class ImageController : BaseController
{
private IMediaService _mediaService;
public ImageController(IMediaService mediaService)
{
_mediaService = mediaService
}
[HttpGet]
public ActionResult List()
{
var RetVal = _mediaService.GetAllImages();
return View(RetVal);
}
}
Here's my scenario:
I've successfully created a custom IIdentity that I pass to a GenericPrincipal. When I access that IIdentity in my controller I have to cast the IIdentity in order to use the custom properties. example:
public ActionResult Test()
{
MyCustomIdentity identity = (MyCustomIdentity)User.Identity;
int userID = identity.UserID;
...etc...
}
Since I need to do this casting for nearly every action I would like to wrap this functionality in an ActionFilterAttribute. I can't do it in the controller's constructor because the context isn't initialized yet. My thought would be to have the ActionFilterAttribute populate a private property on the controller that I can use in each action method. example:
public class TestController : Controller
{
private MyCustomIdentity identity;
[CastCustomIdentity]
public ActionResult()
{
int userID = identity.UserID;
...etc...
}
}
Question: Is this possible and how? Is there a better solution? I've racked my brain trying to figure out how to pass public properties that are populated in an attribute to the controller and I can't get it.
All you have to do is access the ActionExecutingContext of an overloaded OnActionExecuting() method and make identity public instead of private so your actionfilter can access it.
public class CastCustomIdentity : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
((TestController) filterContext.Controller).Identity = (MyCustomIdentity)filterContext.HttpContext.User;
base.OnActionExecuting(filterContext);
}
}
This could be even easier by using a custom base controller class that all of your controllers would inherit from:
public class MyCustomController
{
protected MyCustomIdentity Identity { get{ return (MyCustomIdentity)User.Identity; } }
}
and then:
public class TestController : MyCustomController
{
public ActionResult()
{
int userID = Identity.UserId
...etc...
}
}
You could use a custom model binder...
I can't remember why I used this method over the base controller method #jfar mentions (which is also a good option), but it works well for me and I actually kinda like it because my actions are more self describing through their parameters.
MyCustomIdentityModelBinder.cs
public class MyCustomIdentityModelBinder : IModelBinder
{
public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
if (bindingContext.Model != null)
throw new InvalidOperationException("Cannot update instances");
//If the user isn't logged in, return null
if (!controllerContext.HttpContext.User.Identity.IsAuthenticated)
return null;
return controllerContext.HttpContext.User as MyCustomIdentity;
}
}
Inside your application start event in Global.asax.cs
System.Web.Mvc.ModelBinders.Binders.Add(typeof(MyCustomIdentity), new MyCustomIdentityModelBinder());
Then whenever you have a type of MyCustomIdentity as an action parameter, it'll automatically use the MyCustomIdentityModelBinder.
Eg.
public class TestController : Controller
{
public ActionResult Index(MyCustomIdentity identity)
{
int userID = identity.UserID;
...etc...
}
}
HTHs,
Charles