ASP.NET MVC Share logic between actions - c#

I'm building a site where i need to be able to notify a user on each page. The function is going to look alot like what StackOverflow is using, the yellow(orange?) border at the top. I dont want to reproduce this logic inside all of my actions. DRY.
I'm thinking about using a action filter, but since i need to query my data layer it does not seem to be the best way to solve it.
So, for example, what would be the best way to implement a feauture like "You have a question with bounty ending in X days" on each page?

In ASP.NET MVC 2 there's the Html.RenderAction. For ASP.NET MVC 1.0 if I recall correctly there's the same functionality in MvcContrib.

When I have logic that needs to go on every action, that needs to initialise view data, I do this in my OnResultExecuting method (called after action runs, before view rendered) of my base controller (from which all my controllers inherit), which sets data on my base view data model (from which all my view data models inherit):
public class BaseViewData
{
public string UserMessage { get; set; }
}
public class BaseController : Controller
{
protected override void OnResultExecuting(ResultExecutingContext filterContext)
{
ViewResult viewResult = filterContext.ActionResult as ViewResult;
//Only continue if action returned an ActionResult of type ViewResult,
//and that ViewResults ViewData is of type BaseViewData
if(viewResult != null && viewResult.ViewData is BaseViewData)
{
((BaseViewData)viewResult.ViewData).UserMessage = userService.GetUserMessage();
}
}
}

I would use TempData to store the message, add a helper to render the message (if there is one), and then use that in your master page. This is basically how rails does it, and I have always felt the lack of a notification system (like flash) was an odd gap in ASP.net MVC, just because it is so easy to do.

Related

How would one go about to create or display an image from MVC Controller?

I'm new to MVC and trying to figure out it works. I'm just experimenting with simple projects, and there are somethings that are unclear.
Let's say I want to load an image depending on value (true or false from a checkbutton) from my controller. I have no problem with recieving the value from my view but how would I go in generating this html element?
A few ideas that comes to mind is.
Use IsVisible=false attribute on already rendered image element and then just change that attribute with CSS
Use ViewBag to send data between View and Controller
Use Html.Helper to create a "personalized" element.
What is the way to go in creating element from a Controller and pass it to a View regarding "Best-pratice" ?
Build it like this:
View model/model
public class viewModel
{
public string imgSrc {get; set;}
}
Controller
public class myController : Controller
{
public ActionResult myAction()
{
bool switch;
viewModel vm = new viewModel();
if (switch)
vm.Src = "something.jpg";
else
vm.Src = "somthingelse.jpg";
return View(vm);
}
}
Razor
#model viewMoel
<img src="#Model.Src"/>
MVC is based on seperation of concerns. Read about the design pattern. People often think MVC is a technology, it's not, it's a pattern.
As such it should be used correctly with the correct parts doing what they are designed to do. So just because you can do something, doesn't mean you should. In MVC the views job (concern) is rendering the content. The controller should have no knowledge of this content. It's job is routing and generating of the Model. So all you HTML should be in the view (full stop)

Need to access a variable from all Views

On the site I'm working on, there are users with different permissions on the site.
Given the schedule ID and employee ID that we're currently looking at, we can get their role-specific permissions.
Right now, our BaseModel has a property that properly accesses the DB and grabs this info.
For all views that pass a model to the view, everything runs fine.
The problem lies in Controller Methods where no model is passed. In a few views, all they're supplied is a few ViewBag entries, and work fine.
However, I /need/ the CurrentPermissions property in those pages nonetheless, for the layout. Whether or not the permissions have one boolean value set true/false, something may/may not be displayed/populated.
So, my option seem to be:
Somehow throw my CurrentPermissions into a ViewBag entry for all views, and access them through that instead of the base model.
I'm not sure how to do this. I've seen people using OnActionExecuting, but that fails since my connection to TransactionManager is not yet set up at that point.
Somehow throw just the BaseModel into those views that don't currently pass a model. I'm refraining from this as much as possible. I'm not sure how I would go about doing such, but it seems like that would over-complicate the situation.
How can I go about pushing this CurrentPermissions object (generated from a call to my TransactionManager) to every view (specifically, the Layouts!)
Your approach is what we use in out projects... and we use this approach to systematically remove the use of ViewBag changing it to ViewModels.
Other approach we have used (for UserPreferences in my case) is adding an ActionFilter that ends including the preference in the ViewBag. You decorate the actions needing it with [IncludePreferences] in my case (that is the name of my filter attribute.
EDIT ActionFilter:
public class IncludePreferencesAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
var controller = filterContext.Controller as BaseController;
// IController is not necessarily a Controller
if (controller != null)
{
//I have my preferences in the BaseController
//and cached but here you can query the DB
controller.ViewBag.MyPreferences = controller.TenantPreferences;
}
}
}
In you action you decorate it using [IncludePreferences]
As a temporary solution, I'm doing the following at the top of my Layout:
#{ OurModel.SupervisorRestriction CurrentSupervisorRestrictions = ViewBag.CurrentSupervisorRestrictions ?? Model.CurrentSupervisorRestrictions; }
This way, if we're passing in an object then it works just fine. Otherwise, I'll directly pass in a ViewBag.CurrentSupervisorRestrictions from the controller. There are only a few cases, so it's not that bad.
Better suggestions would be great, though.

General approach for displaying a message on any page

I have a fairly simple (to explain) requirement for my ASP.NET MVC web application:
On any page, be able to display a message based on an event that happened on a preceding page. Messages may include dynamic content, such as information entered by the user in a form submission.
Similar to this...
The approach must work in POST-REDIRECT-GET scenarios.
If the user has two browser tabs (which share session state) open on the application, then any message should only be displayed in the tab in which the related event happened.
For example:
After submitting a feedback form, a message thanking the user (by name)
When logging in, a message telling the user the date/time when they last logged in to the application
My first thought was to use a strongly-typed view with an appropriate model (with a string property) for this. However this would mean changing every single existing non-strongly-typed view to use this model, and updating all existing models to inherit from this new model.
This seems like overkill.
An alternative is to store the message in TempData:
// In my controller
TempData["Message"] = "Some kind of message";
// In my shared master view
#if (TempData["Message"] != null)
{
<div class="message">#TempData["Message"]</div>
}
Using TempData is covered in a bit more detail in this blog posting. Interestingly, the author also suggests using a custom HTTP header as an alternative. An interesting idea, but does not work with POST-REDIRECT-GET.
Is using TempData the preferred option, or is there a "better" alternative? (e.g. something more strongly typed)
My first thought was to use a strongly-typed view with an appropriate model (with a string property) for this. However this would mean changing every single existing non-strongly-typed view to use this model, and updating all existing models to inherit from this new model.
This is the only consistent, reusable, testable solution to do this that I can imagine, despite the extra work it may cause.
It is best practice to use ViewModels to communicate between View and Controllers. You can have a base View Model and all other View Models derived from that as below:
public class BaseVM
{
public string Message{ get; set;}
}
public class CreateViewModel: BaseVM
{
public string CustoomerName{ get; set;}
}
You can populate the Message property while returning the model to the controller as below:
public ActionResult Step2()
{
//Some Logic
step2Model.Message = "Yes, Success..!!";
return View(step2Model);
}
After that, on each view page, you can check if that property has something in it.You can do so as below:
#if(!string.IsNullOrEmpty(Model.Message))
{
//Show message
}
EDIT:
OP is well aware of this approach, but still keeping this answer as it has a snippet to show how to do this in code. Secondly, when it comes to use ViewModels, I agree with following comment by CodeCaster in his answer.
This is the only consistent, reusable, testable solution to do this that I can imagine, despite the extra work it may cause.
You could make a convention in your project that a message to display would always go into ViewBag.InfoMessage dynamic variable. So, in your layout file you would display it if it's passed into the view from a controller.
More strict way would be to create a base model class with InfoMessage property and derive all other models / viewmodels from that base.
To persist the message through POST-REDIRECT-GET scenario, use a session variable which you'd clear once the value is sent to the view - to avoid displaying on more than one page.
Here some code:
public class BaseViewModel
{
public string InfoMessage { get; set; }
}
public class SpecificViewModel : BaseViewModel
{
// other model properties
}
In your controller then:
SpecificViewModel vm = new SpecificViewModel();
vm.InfoMessage = Session["InfoMessage"] as string;
Session["InfoMessage"] = null;
// other code
return View(vm);
Of course your view will have strongly typed model: SpecificViewModel.

Asp.net MVC Page titles in database

Has anyone here structured your views, controller and actions in such a way that the page titles are set from the database. How do you identify one view from another ? In which phase of the lifecycle do you retrieve the page title and set it for the view/action method
a lot of posts are scattered for asp.net, php but then a efficient way for mvc I am yet to find
You could use action filters:
public class ControllerAndActionNameAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
filterContext.Controller.ViewBag.ControllerName = filterContext.RequestContext.RouteData.Values["controller"].ToString();
filterContext.Controller.ViewBag.ActionName = filterContext.RequestContext.RouteData.Values["action"].ToString();
base.OnActionExecuting(filterContext);
}
}
But instead of putting the action- or controllername into the viewbag, you could load stuff from your database based on action and/or controller.
What if you have service or helper method that retrieves all the titles from the database and stores them to some sort of cache or static variable think Dictionary. Depending on how you plan to update with the site running determines when or how.
Then expose a static helper method that the views can call to retrieve their title.
You could probably even incorporate a T4 template to set a property for each view to call the method with the correct retrieval key

Where to put logic for shared items (like menus) with MVC3?

After I found this in our code, I realized a few things:
"get it done now, get it right later" has a limit
I don't actually know where this goes with MVC3
#model int
#using Company.Core.Logic.Financial
#using Company.Core.Repositories
#{
var values = from AccountPlan e in new AccountPlanRepository().GetItemList()
where String.IsNullOrEmpty(e.PromoCode) // filter out promotional plans
select new { id = e.AccountPlanId, name = e.Description };
var items = new SelectList(values, "id", "name", Model);
}
#Html.DropDownListFor(m => m, items)
This specifically is an editor template (#Html.EditorFor(m => m.AccountPlan)), but it made me realize that I don't know where this kind of code goes for common things like menu-builders. If you're using Layouts for MVC3 (and who isn't), where is the code for building a menu across the top based on the user's roles? I'd imagine the view code would be iterating through pre-built menu items and HTML-ifying them, but since the Model is strong-typed, does that mean that all Models need to have knowledge of the menu items?
For once, this is where Webforms made more sense to me, since this would go in the CodeBehind, but I really want to get away from that.
edit: even though I started asking about Layout code, I'm under the assumption it works for EditorTemplates and DisplayTemplates as well. If this is an incorrect assumption, please let me know where these are supposed to go.
edit2: What I ultimately want is to have a clean, possibly even dependency-injectable place to run code that's called from an EditorTemplate. Perhaps this is a case where the EditorTemplate calls immediately into a RenderAction?
Looks like this solves the problem pretty well (see the marked answer, not the original question):
http://forums.asp.net/t/1566328.aspx/1?Building+ASP+NET+MVC+Master+Page+Menu+Dynamically+Based+on+the+current+User+s+Role+s+quot+
Basically, call RenderAction(...) and it will build the model it needs, rather than forcing you to have every model require a list of menu items.
For me personally, I do a lot of menu filtering based off of active directory groups so I need to know what their access levels are across the entire application.
I create a new controller that I call ControllerBase
public class ControllerBase : Controller
{
//authorization group setting an menu creation here.
//set properties and objects to ViewBag items to access from the front end.
protected override void Dispose(bool disposing)
{
_db.Dispose();
base.Dispose(disposing);
}
}
and then on all the other controllers in my project I just extend from ControllerBase
public class HomeController : ControllerBase
{}
This keeps all my server logic in one file for managing permissions and gives all my pages access to these variables when I need to hide or show different ui elements based on permissions.
Child Actions are perfect for this scenario. The logic required to generate the view is wrapped up in a controller action, like normal, and the view that wants to make use of the child action simply renders the action..
You can also cache these partial views, which would make sense for something like a main menu - as presumably the users permissions won't change that often.
e.g.
[OutputCache(Duration = 300)]
[ChildActionOnly]
public ViewResult MainMenu()
{
var model = GetMenuModel();
return View(model);
}
The view that wants to render the child action does so like this.
#{ Html.RenderAction("MainMenu", "Account"); }
And thus the view calling the ChildAction has no need to know what model the view requires.
There are also overloads on RenderAction, should your child action require you to pass parameters to it.
You shouldn't (have to) access the Repository inside the View. That belongs in the Controller.
And the Menu is implemented in the Masterpage, you don't give much details on specifics.

Categories

Resources