MVC Web Application Database Driven Menu by User Permissions - asp.net-mvc

We are going to port a legacy windows app to a large web application for a vertical market. Looking at MVC. Each implementation may have 50 to 5000 users. Looking at putting navigation in Master Page. The application will contain 200 to 300 menu items, resulting in over 500 views. We want to display a trimmed navigation menu for each user based on their application permissions. A user may see only 20 items, or all available.
Most posts I have seen suggest passing navigation items to Master Page through viewdata, established in a base controller class. I understand this.
Each of the potentially 10's to 1000's of users will have a different set of permissions.
Does anyone have any solutions that will avoid hitting the database to get the users menu items on every controller request that inherits from the base controller?
Is there a caching scheme that will work for each user?
Should the navigation be handled in a frame (not my choice)?
Is this just a price we will pay for this approach to navigation?
Thanks for any input!

You could start by caching linq queries which would be a nice way to tackle this at the DB tier.
Doing this in MVC using an action filter wouldn't be too hard either.
I implemented something like this in PHP a year ago but the general idea is the same. Firstly, you'll need to assign each menu configuration a unique id. This way when user A and user X request the same menu configuration, it resolves to the same cache file.
The first time a menu needs to be loaded for the user, it is loaded from the database and passed to the user. Simultaneously, it is saved to a cache file with the unique id in its name. On subsequent requests the action filter can load the data from the cache file if it exists and bypass the database.

Some ideas:
1.) You could have your nav bar html come from a Html.RenderAction (MVC Futures) and use the Output cache on that.
2.) You could generate the html for the nav bar per user then save that to the DB and regenerate if their user permissions change. So all you would need to do is pull the html from the DB against each users record.

Related

"State" management for asp.net mvc multi partial view + ajax app

I am trying to convert my asp.net mvc4 app, which had fairly heavy use of SessionState, into a stateless app. I understand that I can store this information in the DB, and intend to do so.
My question, though, is about my particular architecture. My app has a main 'page' consisting of a number of partial view panels, which each have actions in them that can affect the other panels. What i've been doing up to now is storing the entire state of the viewModel (lots of inter-related EF list collections and 'record' objects) in the session, and its been working great. Except when the session just randomly dies.
So, I need to get this data out of the session, and into the DB where I can rebuild the thing at need. My concern is that, if I store the info in the database, every single action done on screen might affect 3-5 different panels, each with their own State updates, thats a minimum of 10 round trips to the DB for every interaction!
What are some strategies I can use to make this idea more scalable?
EXTRA INFO
The view in question here is a sort of POS shopping cart system. There are panels for selecting events, selecting/adding items to the cart, editing cart items, selecting contacts, editing contacts, displaying the cart items, displaying the cart 'subtotals', and finally, a panel with a [checkout] button.
Selecting a new event will change the list of available items. Selecting an item to add to the cart will change the cart item list, subtotals, as well as the checkout panel. Same for editing a cart item.
The main concern is how to recover from a lost session, as I've found the built-in asp.net session code too unreliable. My testers have encountered issues with sessions timing out, and then my app not having any kind of recovery process. When its installed on 1500 sites, each with an average of 10 users, its going to be a plague of lost session issues, and I need to combat that before it becomes a real problem.
I agree that I'm not going stateless...wrong choice of words used in a rush. I'm just trying to move that state into a form that I can rely on past the session failure. My main idea presently is to continue using the session as the local cache for the viewModel data, but to have a fallback operation that can rebuild the viewModel from DB if the session one is lost somehow.
You shouldn't necessarily be using a database to store (what sounds like) data that only needs to be persisted in the short term.
If these changes to the other partials are only relevant in the context of the current "master view," then I would suggest using jQuery AJAX to send off the requests, parse the response JSON and update the other views. Tutorials on jQuery AJAX and ASP.NET MVC are easy to find, if you don't already have the knowledge:
http://www.codeproject.com/Articles/41828/JQuery-AJAX-with-ASP-NET-MVC
This way, you don't need to make a bunch of round trips. If the changes need to be persisted beyond the context of the current view, make ONE round trip to the database to perform the update and then simply update all of the other partials from the in-memory response from the AJAX call.
You don't need to read from secondary storage multiple times when you already have all of the information you need in-memory. Just do the reading and writing once.
I decided to go with a hybrid approach. I'm still using session, but I'm building out a DB 'recovery' option, so that if the session portion is lost, the DB will be able to provide the values needed to rebuild the session seamlessly.
Seems to be working well, so far.

Durandal+Knockout to develop a Single Page Application: Selectively show portions of page based upon authorization

I am using Durandal, knockout to create a Single Page Application. I need to do following (two pretty simple things):
Show/hide widgets that are only for administrator, based upon the user's authorization,
Change menu options based upon whether user is authenticated or not (for anonymous show - login/sign up and when authenticated show "Welcome .." .
If this was a regular MVC4 application I would have done it using
#if (User.Identity.IsAuthenticated) { ... } check in razor views, but with views in durandal this is ruled out.
I want to avoid putting sensitive business logic in javascript - user need not know what kind of options could have been available to him if he was an administrator.
What's the best way to achieve this in Durandal & Knockout? I have been coding so far using classic ASP.NET and lately using ASP.NET MVC. Developing SPA using Durandal is a new game for me...If anyone can give me only steps/pointers to do this that will help a lot too..thanks in advance!
The way I do it.
My menus are build inside a menubar.js file. The menuItems are observableArrays([]) initially and I subscribe to a topic "user-logged-in".
When the user logs in, I get the user's permissions/roles and store them locally in storage. and then send out notification "user-logged-in" with the user data.
My menubar recieves the notification, checks the permissions/roles and adds various menu items appropriately.
the shell.html has a view composition for the menubar.js. So if there are menuitems, it shows up, else it does not. so when the user logs in, the menuItems are populated and at this point the menu items show up.
When the user logs out, I clear the local storage cache and send out a message "user-logged-out".
The menubar.js recieves this message and clears it's menu items, essentially clearing the menu on the menubar.htm
You can essentially do the same for the widgets and use a visible binding to a property which hides or shows for a particular permission/role.
Also important is router.guardRoute. read up on this so that people cannot directly go to a route without logging in.
Hope that helps

How to persist a model across multiple requests in ASP.NET MVC 2

I'm building a web application that has a particular model representing some events. Users need to be able to add N number of people to a given event. Choosing people is handled by a partial view.
I'm trying to build a menu that displays when users click "add a person" to the event. Because the event hasn't been filled out completely yet, there is nothing in the database to persist between requests.
I also have validation logic on the event page.
My proposed solution is to add the form to search or add for people on the event form itself and have a submit button that sends the values that have been added back to the server, where I can store them in ViewData or Session.
Unfortunately, doing this flags the validation.
My second solution is to load a partial view responsible for loading the UI to add/search for a person. I could add a little code on the method in the controller that returns a partial view storing the existing data in a session variable or viewdata. Trouble is, I have to submit the form to do it--again tripping the validation!!!
I'm wondering if perhaps I chose the wrong tool to do this...because in webforms, there would probably be a postback and you would just perform an operation on that postback. I'd like to avoid rewriting the application in webforms and am wondering if there are ways I'm overlooking in ASP.NET MVC.
Thanks in advance for the ideas!
I would probably have the partial view send it's data to the main page (with javascript). That way there is only one post to the server and it is when all of the data the user needs to enter has been filled out. How are you displaying the partial view? Is it on the main page (in a div), or is it a separate pop-up window? Either way, you should be able to use javascript to store this data on the main page and post all of the data back at one time.
HTH

Session and Tabbed Browsing ASP.Net MVC

I'm once again looking into the world of tabbed browsing and Sessions. Looking over a few google searches it seems that there isn't a nice way of supporting this.
Does anyone know of a method that allows Bookmarking without stealing a session (cookieless) (and this doesn't work in MVC2 for dataannotations).
Supporting tabs in such a way that it's per use case (like Windows Workflow), going through two workflows at once.
I'm thinking a url in the query string might support this, but I'm wondering if anybody else has done a similar implementation.
[Edit] Use Case: Say I'm writing an application that uses something like Windows Workflow. Each UI workflow may do an action such as collect settings of a page and execute some external process. I may wish to do two of these workflows at once (not necessarily the same UI workflow). As such if I saved in session I would get:
a) Different tabs interfering with the workflow
b) Previous/Next buttons would be extremely difficult to work out, due to a).
I would like it so either, a user cannot open another tab to a url (don't think there is a 100% method of preventing this), or allow a user to use a UI workflow in isolation without one affecting another (much like running two workflows in two different browsers).
Hopefully that gives an indication of what I'm attempting to do.
Regards,
J
It sounds like you might be trying to do the following:
For example, let's say you have a two page questionaire, the first page has first name on it and the second page has last name on it. You desire that the user can open two tabs, and be at different pages in the questionaire while entering different data in the questionaire in each tab.
So in Tab A, you have entered Mark as the first name and submitted and you are at page two now in Tab A. You decide you are going to do a questionairre for your friend also, so you open up a new Tab, Tab B. In Tab B you enter Tom and submit the page.
Currently in the browser you have Tab A, which is at page 2 of the questionaire with firstname = "Mark" and Tab B which is at page 2 of the questionaire with first name = "Tom". Assuming you wanted to maintain both of these in session on the server here is an approach that i think will work for you.
When a web browser requests page 1 of your form, on a GET request(no posted questionaire data to the server), you supply a hidden field in the the response html and generate a random number to store in that field. When this form is submitted you do the following on the server:
Look in session using the random number as a key "var questionaire = session[Request.Form["questionaire_rnumber"]]
if the questionaire is not in session you create a new questionaire and update it's properties and stick it in session
var questionaire = new Questionaire();
questionaire.FirstName = Request.Form["firstName"]
session[Request.Form["questionaire_rnumber"]] = questionaire;
if the questionaire was in the session you simply update the object, and display the next page, however when you display the next page you will want to supply the hidden random number field in the html again, using the same random number you used on page 1.
This way you can hold any number of questionaires in a single session. With MVC.NET it should be straight forward for you to add the random number field to your view model and add the logic for looking in session for an existing questionaire or creating a new one and I think you'll be good to go.
You should keep in mind the possible issues with the approach also, like back button issues, security issues, and performance issues.
One example of a security and a performance issue would be that an attacker realizes your application works like this and the attacker requests page 1 of your form 10,000 times and submit the page 1 each time. You would have 10,000 questionaire objects in that one user session. If the attacker deleted his session ID cookie 10,000 times and for each session id cookie he created 10,000 requests for page 1 and submitted the page 1 form, you would have 100,000 questionaire objects cumulatively across 10,000 sessions on your server. So you should put some constraints on it also to protect your application, for example:
Any individual session can only have X questionaires in session
Any individual IP address can only have Y concurrent sessions (this you would probably need to track in the Application object)
ADDITIONAL RESPONSE TO ADDED USE CASE
Thanks for the use case. My solution should still work for you. You have two options.
If you want to ensure there is only one tab working with your workflow, then when the random number is passed to the server from a new tab you will be able to detect that there is another workflow in progress and that the random number from the new tab does not match the random number from the first, so you will throw an exception and show the user some messaging that says they can't start a new workflow until they finish the first one, and ask if they want to cancel the first. You have to ask if they want to cancel it because if they close their browser on the first workflow they started they will be stuck until their session expires. Which won't happen if they keep trying to start a new workflow.
Secondly, you could allow them to do multiple, but segment the context of each workflow by the random number, as suggested in the first answer. The whole point is that you are making little mini-sessions in your session, but keyed of a value that is only stored in the client. So since each tab has a different random number when the form posts to the server, it's easy to correlate that random number with an entry in your session that has all the information about the workflow initiated from that tab.
Hope this helps.
You need to store wizard state information in the client in some way, via query string or form values. As you've intuited, Session will not work. Nor will anything else that relies solely on what is on the server.

How to pass context around in a ASP.NET MVC web app

Ok, I'm a newbie to ASP.NET web apps... and web apps in general. I'm just doing a bit of a play app for an internal tool at work.
given this tutorial...
http://www.asp.net/learn/mvc-videos/video-395.aspx
The example basically has a global tasklist.
So if I wanted to do the same thing, but now I want to maintain tasks for projects. So I now select a project and I get the task list for that project. How do I keep the context of what project I have selected as I interact with the tasks? Do I encode it into the link somehow? or do you keep it in some kind of session data? or some other way?
As it sounds like you are having multiple projects with a number of tasks each, it would be best practise to let the project be set in the URL. This would require a route such as "/projects/{project}/tasks". It follows the RESTful URL principle (i.e. the URL describes the content).
Using session state will not work if a user possibly have different projects open in multiple browser windows. Let's say I am logging into your system and a selecting two projects opening in two tabs. First the session is set to the project of the first opened tab, but as soon the second tab has loaded, the session will be overwritten to this project. If I then do anything in the first tab, it will be recorded for the second project.
I use:
Session state for state that should last for multiple requests, e.g. when using wizards. I'd be careful not to put too much data here though as it can lead to scalability problems.
TempData for scenarios where you only want the state to be available for the next request (e.g. when you are redirecting to another action and you want that action to have access to the state, but you don't want it to hang around after that)
Hidden form fields [input type="hidden"] for state that pertains to the form data and that I want the the controller to know about, but I don't want that data displayed. Also can be used to push state to the client so as not to overburden server resources.
ok, From what I can tell, the best option seems to be to save it into the Session data
RESTful URLs, hidden fields, and session cookies are your friends.

Resources