Persistent data across internal pipelines / requests - asp.net-mvc

Basically I have a webpage structure where common parts of the page (header, side bar, etc) are generated in a separate controller filled with child actions outputting partial views. I then call these actions (using RenderAction()) from the layout page of the website.
So (if I'm right in saying this), There are multiple internal mvc pipelines (header/sidebar internal requests) including the original request pipeline to for the specific webpage. How/Where can I initialize some data from the original pipeline request and have that data accessible from the other internal mvc pipeline requests?
Summary of what I want to accomplish (with example)
Request for website comes in.
MVC starts pipeline for "Home" controller, "index" action.
Before the Action gets executed, some data needs to be created that can later be accessible.
From the layout page, several "RenderAction" Methods get executed creating sub pipelines for interal requests (e.g. "Shell" controller, "DisplayHeaderBar" action
"DisplayHeaderBar" needs to access some data that was set in step 3 before rendering partial view
Hopefully this makes sense...

What you're looking for are child actions. You simply create an action in some controller that returns a partial view. For example, you could handle your site navigation via:
[ChildActionOnly]
public ActionResult SiteNavigation()
{
// get the data for your nav
return PartialView("_SiteNavigation", yourSiteNavModel);
}
The ChildActionOnly attribute ensures that this action can only be called as a child action, making it inaccessible via typing a URL in a browser's navigation bar.
Then, you create a view in Views\Shared\_SiteNavigation.cshtml:
#model Namespace.To.ClassForSiteNavigation
<!-- render your site navigation using the model -->
Finally, in your layout:
#Html.Action("SiteNavigation", "ControllerWhereThisExists")

I think you could use Tempdata for that. Tempdate gets deleted after you access it, so if you want to use the data more then once use Tempdata.Peek or Tempdata.Keep.
Here is a link with some explanation how you can pass data in asp.net mvc.
https://msdn.microsoft.com/en-us/library/dd394711%28v=vs.100%29.aspx
If tempdata doesn't do it then you could use cache.

Related

how to manage logic in the _Layout.cshtml

I am currently working with an mvc site where I have a fairly extensive main layout page. It is dependent on data from the database which in turns includes inherent logic as what to include etc on the layout.
Most of my controller actions are rendered within this layout. I am not sure of how to work this. Being used to master pages in web forms, all the logic resides in the master page. I have a couple mechanisms to achieve the common layout logic but looking for the best practise way of achieving such.
Options are:
Replicate logic in all controller actions (not really an option)
Extract the logic in to another class and call it from the controller actions
All controllers inherit from a base controller which as overrides the OnActionExecuting method and us this to perform logic and return the necessary data in the TempData
Use "RenderAction" in the cshtml to call necessary controller actions and extract the layout in to smaller partial views
Any other options open to me or recommendations?
If I understand you correctly, I would use an action partial
#Html.Action("{ActionName}", "{Controller}", new { roleName= "Admin" })
Action Partials call dedicated controller action methods of type
[ChildActionOnly]
public ActionResult _TopNav(string roleName)
This way you can design recurring logic that will propagate throughout your application without replicating.
Even better, if your _Layout handles privilege based link generating, you can pass role id's and control what the end user sees and what their navigation buttons point to.

Call js from MVC Controller

I have a web site written using ASP.NET MVC3.
The site has one single web page and what I want to do is to have the controller handle the request, do some parsing and processing and thereafter trigger a certain javascript method in the single webpage based on part of the data from the request.
How would I do this in the MVC controller?
You can't directly trigger actions in the View from the controller.
However, what you can do is return an ActionResult from the Controller Action to the View that instructs the View to execute your Javascript.
This could be a PartialView that includes the Javascript (in which case, you'll need a placeholder in your view for the PartialView to be rendered 'into') or something like a JSONResult that contains a property instructing the logic within the View what to do.
Either way, the call to the Controller Action is going to be triggered by client-side Javascript and your desired logic executed when the Controller Action finishes executing.
You'll probably find it easier to use jQuery.

Shared 'per request' storage for ASP.NET MVC controllers

In a standard ASP.NET webforms project, if I have a several user controls on a page that require the same data I'd tend to have the first user control put the data in the HttpContext.Current.Items collection so it was available for all the others.
What's the nearest equivalent to this for a ViewResult on an MVC controller that's used multiple times on the same page?
Thanks
Edit:
Basically the ViewReults here are content areas which appear more than once on a page, so I was looking to query the database once to get a collection of all the content for the page (when the first content area is laoded) rather than a separate query for each content area. Thought it would speed things up.
Further info:
Just to make it clear, the content controller action is used repeatedly as a 'RenderAction' from within pages that are served by an additional 'main' controller. Consequently I don't think I can use ViewData (or any other property on the controller class itself) as it's not shared between different instantiations of the same controller.
I think I'm going to have to fall back on the HttpContext after all using a method such as this one.
"Multiple times on the same page" means the same request meaning you can still use the HttpContext.Current.Items collection.
Why don't you just put this content in your ViewModel and have all the controls use that? This is a pretty standard usage of a View's model.

Is it i possible to prevent certain PartialViews from being served if requested directly?

I'm working on a site that has routes to actions which render Partial Views. A lot of these partial views are components which together make up a complete page.
For instance on a search page I'm working on has a text box, a list of tabs, and a Table.
Seach of these can be accessed with a URL similar to
/Search/SearchPanel
/Search/Tabs/{SearchTerm}
/Search/ResultsTable/SearchTerm?tab=[currently selected tab]
and these are all rendered on with a RenderPartial on my Index page.
When the page loads, it will display each of these components the way I want it. But at the moment there's nothing stopping a user from going directly to the url
/Search/Tabs
to render only a tab control which is meaningless outside the context of the rest of the elements on the page.
Is there a way for me to prevent this?
Have you tried marking your Controller method as private?
private PartialViewResult MyPartialResultMethod()
This should allow you to call it from within your code to build up your pages and disallow any public access such as through a URl.
I'm testing this now to make doubly sure my answer is correct so I'll update as I test.
In your tabs example you could simply restrict access by using a second controller method for Tabs that's private.
So you'd have something that looks like:
public ActionResult Tabs(string searchTerm) // When a search term is passed.
and
private ActionResult Tabs() // When no search term is passed.
You could create an ActionFilter which checks if the Request.IsAjaxRequest() is true. If it's not (meaning the user is calling the view directly), re-direct accordingly.

Where to apply logic for a sidebar control in ASP.NET MVC

Take the example of wanting to have a "Latest news items" sidebar on every page of your ASP.NET MVC web site. I have a NewsItemController which is fine for pages dedicating their attention to NewsItems. What about having a news sidebar appear on the HomeController for the home page though? Or any other controller for that matter?
My first instinct is to put the logic for selecting top 5 NewsItems in a user control which is then called in the Master Page. That way every page gets a news sidebar without having to contaminate any of the other controllers with NewsItem logic. This then means putting logic in what I understood to be the presentation layer which would normally go in a Controller.
I can think of about half a dozen different ways to approach it but none of them seem 'right' in terms of separation of concerns and other related buzz-words.
I think you should consider putting it in your master page. Your controller can gather data (asynchronously, of course), store it in a nice ViewModel property for your view (or in TempData) and then you can call RenderPartial() in your master page to render the data.
The keeps everything "separate"
http://eduncan911.com/blog/html-renderaction-for-asp-net-mvc-1-0.aspx
This seems to address the question - even using the instance of a sidebar - but using a feature not included with MVC 1 by default.
http://blogs.intesoft.net/post/2009/02/renderaction-versus-renderpartial-aspnet-mvc.aspx
This also indicates the answer lies in RenderAction.
For anyone else interested, here's how I ended up doing it. Note you'll need to the MVC Futures assembly for RenderAction.
Basically you'd have something like this in your controller:
public class PostController
{
//...
public ActionResult SidebarBox()
{
// I use a repository pattern to get records
// Just replace it with whatever you use
return View(repoArticles.GetAllArticles().Take(5).ToList());
}
//...
}
Then create a partial view for SidebarBox with the content you want displayed, and in your Master Page (or wherever you want to display it) you'd use:
<% Html.RenderAction<PostController>(c => c.SidebarBox()); %>
Not so hard after all.
You can create a user control (.ascx) and then call RenderPartial().
Design a method in your controller with JsonResult as return type. Use it along with jQuery.
Use RenderAction() as suggested by elsewhere.
News section with ASP.NET MVC

Resources