Pass data to the view. Always through viewData ? - asp.net-mvc

I’m building an application that monitors other systems. Now I want to implement partial view, a User Control called “status”. This control shall display status information about the application.Like:
user logged in,
How many systems online,
Latest activity.
This partial view shall be rendered in nearly all other views. How shall I pass this information to the view?
I don’t want to write
Wiewdata[“SystemsOnline”] = Helpers.CountSystemsOnline()
Wiewdata[“SystemLatestActivity”] = ………………
in all my actions.
Can I write something like Html.RenderPartial(../Shared/Status) that fist go to an action that adds the viewdata?
Or shall i access the information directly in the view trough the hepler?
I noticed that the defult LogOnUserControl view use Page.User.Identity.Name to directly access that data.
When is it ok to not pass data throug viewdata in the controller?

You have to use the ViewData class for that purpose. I do not recommend to invent tricks instead.
But you do not need to manually pass the same data from every action. Just do it in your base controller:
protected override void OnActionExecuted(ActionExecutedContext context) {
base.OnActionExecuted(context);
if (context.Result is ViewResult) {
// Pass the systems online to all views
ViewData[“SystemsOnline”] = Helpers.CountSystemsOnline();
}
}
Only a couple of lines of code and you make it available everywhere.

Related

Send data between 2 controllers MVC5

My problem: I have a list A selected from database of a controller A, controller A will display list A. In view, each record of list A has a link call controller B (#html.ActionLink("text","actionB","controllerB", new {id=listA.ID})) and i want to send list A to controller B as well. I've searched about how can send data between 2 controller, there are 2 ways are:
Using tempdata
Using session
But all of those above is not really suitable on this case, can anyone tell me other ways to solve my problems. Thanks
A better alternative would be to retrieve the list again in your second controller. This is generally pretty cheap and fast.
This is what I do. For things like lists I may re-use in several places I utilise the Initialize method in each controller then check the Action name to determine which lists of objects I need to load.
protected override void Initialize(RequestContext requestContext)
{
base.Initialize(requestContext);
//if this is the insert or edit form - get the lists for the d.down form inputs
var actionName = requestContext.RouteData.Values["action"].ToString().ToLower();
if (actionName == "insert" || actionName == "edit")
{
var _uow = new UnitOfWork();
var lstMyList = _uow.MyRepository.Get().OrderBy(n => n.title).ToList();
_uow.Dispose();
ViewBag.MyList = lstMyList;
}
}
Obviously the code to retrieve the list may differ depending on how you access your DB but you get the idea. This way, you only call in one place within each controller's code and yes, it may result in more database calls but for me, I like the neatness of not having calls littered all through my actions. MyList is available in the ViewBag to populate form controls or output where needed or for in-view logic if you're that way inclined.
As the application grows there will be many requests you will need to make requests to the db, do not worry about such calls to the db.
However to answer your question...
You can store it in session/tempdata and then retrieve it. This will increase your server memory usage, what if the user didnt have to look at the detail. It's just a waste.
You store it in the page it self as json and then post it to the server and pass it to the second controller, but the data can be manipulated on client and also increases the bandwidth, making the page slower to download and upload.
Becomes difficult to maintain.
In short retrieve it from db from the second controller.

Share one action with two views

I'm working on a website which needs a control panel that is used by multiple user types. I've been wondering is it possible to make different views that have different layouts but they are similar in operation use a single action so that I wouldn't need to re-write them again?
For example, both Admin and Supervisors can modify employee details but the admin views have a different layout than the supervisor. I can write different actions for each view, but the code in both actions would be exactly the same as they do the same operation. So can one action be shared with many views ?
You certainly can. However, the approach depends on how you determine which layout to present and where that information comes from. The most straight-forward approach is to determine the type of users (Role) and based on the user's role you can render a specific view within the same action method...
public ActionResult YOUR_ACTION()
{
//do your processing
Role role = GetCurrentUserRole();
if(role.Name = "Admin")
return View("ViewForAdmins");
else if (role.Name = "Supervisor")
return View("ViewForSupervisors");
else
return View("EveryoneElseView");
}
Maybe you could put employee details in a partial view and call RenderAction from main views.

How to keep views free of authorization logic in mvc?

I have a view to display a list of items.
The user can edit, delete or create new items, but according to their authorizations they may or may not be allowed to do some of this actions.
I have the requirement to display only the actions which the current user is allowed to do, but I don't want to clutter the views with authorization if-else's
Despise of being a very common requirement, I can't find a real satisfactory way of doing it.
My best approach so far is to provide an overload to the Html.ActionLink extension method that takes the permission to ask for, but there are going to be more complex scenarios, like hiding entire blocks of html or switching a textbox for a label+hidden.
Is there a better way to do this?
One example I can think of would be to invoke Html.RenderAction (link: http://msdn.microsoft.com/en-us/library/ee703490.aspx), and then pass the link you wish to use as a route value to the action method (it will appear as a parameter on your action). Because it's a RenderAction, you get to go back through the controller process and thus you can have it render whatever view or data you want depending on the user's state.
Example:
<% Html.RenderAction("Permissions" /* Controller */, "PermissionLink", new { Url = "Admin.aspx" }); %>
And your controller would have something like:
public ActionResult PermissionsLink (string url)
{
// Do Whatever kind of Authentication you want here, Session is available, etc
if(authenticated)
return View("Link");
else
return View("Blank");
}
I personally don't see anything wrong with this kind of conditional logic within the view. The logic is still all about presentation. You decide whether to show or hide, enable or disable, highlight etc. This is the view's job, not the controller's. As long as the view does not need to actually compute anything to come to its decision. The controller should be agnostic of the view's implementation as much as the other way around.
My approach would be this:
Make the controller do the actual logic of deciding the level of access that the user has
Pass this information to the view using (ie via the ViewModel) but in a way that is neutral to implementation details (ie "user is admin", "user is an author", etc)
Let the view draw itself appropriately using only the pre-compiled data that is provided by the controller.
This also has the benefit that the view itself can be removed and replaced without any effect on the controller.
We had this same issue. We ended up writing a lot of helper methods and in cases where lots of html output was required, we put them in partial views.
Would it not be simpler to create several views with various controls based on what values the user can change, then return the right view based on the user's access rights?
Views should be for presenting information only, there really shouldn't be any conditional logic contained in them, or at least a bare minimum.
I've always found when i get to the point where i'm finding it hard to grok a situation like yours, the best solution is always to go back to the controller and e-assess what i'm passing to the view in the first place.
By the time the View is called to do its job, all of the important 'decisions' should have already been made.
In complicated situations where are many conditions and rules I'm doing this that way:
ViewModel
public class ModelView
{
private IAuthorisationService { get; set; }
public bool CanShow
{
...
}
}
View:
<% if(Model.CanShow) { %>
<html>
<% } %>

ASP.net MVC - request-scoped global variable

I have a value which I want to be vaild during a single request. I am not using Session, as this would make the value global for the entire navigation session.
So I have put thie value in a static field of a class. Great, but then I discovered that such fields are even more global, that is, they stay set for the entire application! This means that there could be random interaction among navigation sessions.
So the question is: is there a safe place I can put a global variable, which will be
global throughout the request
reset after the request is completed
not affected by any other request, either of the same user or by other users
Thanks
Palantir
EDIT
I'll elaborate. I have a piece of code in my master page, which I need to hide on certain conditions, of which I am aware in the controller only. I thought about setting a static variable in the controller, which then would be queried by the master page, but now I see there could be a better way...
Use HttpContext.Items - a per-request cache store. Check out this article on 4guysfromrolla for more details.
It should work fine in ASP.NET MVC. You may wish to derive your master page from a base class (either via code-behind or using the Inherits directive) and have a protected method on the base class that inspects HttpContext.Items and returns, e.g. true/false depending whether you want to display the conditional code.
TempData lasts until the next request as already noted.
But there are also two other dictionaries scoped to the single request.
{Controller,ViewPage}.ViewData
Context.Items
To communicate from controller to (master) page ViewData is probably the better choice.
Two approaches come to mind:
Create a base controller where you set this variable, and then have all your controllers inherit from that.
Use TempData - the problem here being that it sticks around for the next request. But maybe knowing that, you can work around it by using a GUID key to determine that you are, in fact, getting a new value when you need it.
I would probably go with 1).
The common way to access data in a MasterPage that is set in Controller (Action) is via ViewData["TheDataKey"] = "SomeValue".
This is relatively easy and there are a couple of ways that you can do it - depending on how your site works.
I'm interpreting your request as that you want a property or variable that exists for the duration of the request and is visible to the controller, model and master.
A static property is visible to the current application in ASP this means a load of users connecting at once, but not necessarily all of them. IIS will spawn new ASP applications as it needs to.
So the ways you can do this:
You can have a custom base class for your master page or a code-behind page (as all the WebForms stuff still works)
You can have a custom base class for your controllers.
You can get to one from the other, so:
void Page_Init( object sender, EventArgs e )
{
var ctrl = this.ViewContext.Controller as MyBaseController;
if ( ctrl != null )
{
MyLocalProp = ctrl.PropOnMyController;
}
}
This will then be available in the controller and the master page on a per Request basis.
Did you look into the tempData that is attached to the controller class? it is a simple dictionary that preserves it's value through one single request.That would meant that your data can only be accessed in the controller but that should not be a problem.
public class MyController : Controller
{
[AcceptVerbs(HttpVerbs.Get)]
public ActionResult MyAction(string id)
{
this.TempData["Message"] = "YourData";
}
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult MyAction(string Id)
{
var myData = this.TempData["Message"];
}
}
This works for me. I use it only to display warning messages and stuff like that.

Where to put master page's code in an MVC application?

I'm using a few (2 or 3) master pages in my ASP.NET MVC application and they must each display bits of information from the database. Such as a list of sponsors, current fundings status etc.
So my question was, where should I put these master-page database calling code?
Normally, these should goes into its own controller class right? But then that'd mean I'd have to wire them up manually (e.g. passing ViewDatas) since it is out of the normal routing framework provided by the MVC framework.
Is there a way to this cleanly without wiring ViewData passing/Action calls to master pages manually or subclassing the frameworks'?
The amount of documentation is very low... and I'm very new to all this including the concepts of MVC itself so please share your tips/techniques on this.
One way to do this is to put in the masterpage view the hook for the ViewData and then you define a BaseController : Controller (or multiple base classes) where you do all the db calls you need.
What you wanna do is quite the same thing described in this articles.
I hope this helps!
Regards
Great question. You have several options available to you.
Have a jQuery call on your masterpage that grabs the data you need from a controller and then populate your fields using jQuery again.
Your second option is to create user controls that make their own calls to the controller to populate their information.
I think the best choice is creating controls for the region of your masterpage that has data that needs to be populated. Thus leaving your masterpage to strictly contain design elements. Good luck.
If you don't mind strongly typed view data, you can put all the master page data in a common base class for viewData. You can set this data in the base class's constructor. All your views requiring additional data will then need strongly typed viewdata that inherits from this base class.
To allow a call to View() in your controllers without any explicit viewdata you can override View in your ControllerBase:
protected override ViewResult View(string viewName, string masterName, object model)
{
if (model == null)
{
model = new ViewDataBase();
}
return base.View(viewName, masterName, model);
}

Resources