Prevent caching of layout file in MVC when caching Index action method - c#

I have many create Views in my MVC application. I have cached the GET Index action method using [OutputCache] annotation, so that the view is stored in the cache.
I store the details about the currently logged in user in the Session, and display the user's first name in the layout after reading it from the Session.
The problem is, since I have cached the views, the layout is also cached. So even if a different user logs in, the first name of the previous user is visible, because the layout was cached last time.
Is there any way to prevent caching of the layout? Or is there any other way I can stop the first name display from getting cached?
I thought about using VaryByCustom, but I am not sure what to do in the GetVaryByCustomString method that I will need to override.
What would be the best approach to prevent caching of the layout, or alternately, varying the cache by user?
EDIT:
I must clarify that I am using my own custom user management logic. I have my own Users table in the database and I retrieve relevant data on login and store it in Session.

Here's one way you could use the GetVaryByCustom function in Global.asax.
public override string GetVaryByCustomString(HttpContext context, string custom)
{
var varyString = string.Empty;
if(context.User.Identity.IsAuthenticated)
{
varyString += context.User.Identity.Name;
}
return varyString;
}
What you could also do is get the user information with an ajax request and insert it using javascript, that way you don't need to recache the page for each user. Personally I would go with this approach if the username is the only thing that differs.

Related

Handling Parameters of ASP.NET MVC Application

I am working on ASP.NET MVC 4 Project of our Company. It is revenue based application and It has some Filters at every Controller and Action of the application.They are Year and Quarter like some filters. for these filter changes I used Create Base Model, (It contains these filter values) and every other Models inherit from this Base Model.Base Model is use at #layout view.
Every change of filter should update the view.
Base Model in layout view something like this
public class BaseModel
{
public string YearSelected{ get; set;}
public string QuarterSelected{ get; set;}
}
and other Models are inherit from this BaseModel
public class RevenueModel:BaseModel
{
// other stuff
}
For all these things I am sending through the parameters.Seems like now Parameters are increase requirements are changes to add more parameters
1.I want to know the method that handle is correct and If method is insufficient Suggest me best
way to handle this parameter changes.
2.In the UI(View),
When user change the view by checking radio button and click on apply filter button I am using jquery for handle this,
window.href='Url Action with new Parameters';
window.href='#Url.Action("SomeAction","Controller",new{ // those all parameters } ';
When button click window.href will automatically call and update the view I want to knowIs this method Robust? Suggest me best way to handle this scenario.
"Simply I need a answer for if i use jquery to call an action and use high numbers of parameters for the call controller action"
What you're doing is doable, but as #ps2goat points out, you can run into issues if you have too many GET parameters in the URL.
But the right answer depends on how the filters will be used. Will the user often change the filters or will he rarely change them? If the user is expected to browse through your app and only rarely change the filters, I would suggest you to use another approach than including the parameters as GET parameters in the URL because:
You could run into problems if the total length of your URL becomes too long, as #ps2goat points out.
You could run into user experience problems. If a user bookmarks a page, and then later changes his filters, and uses the bookmark to return to the earlier page, his filters would be reverted, which is probably not what he would have expected.
It wouldn't look very pretty. All your urls on your site would look like /controller/action/?YearSelected=2014&QuarterSelected=1&Parameter3=2&Parameter4=8, which could also create SEO issues you would need to take care of.
In that case, I would recommend you to consider using a cookie or saving the user's filters on the server instead. (But preferably not in a Session, as that can create scalability problems for your application). If you used a cookie, the user's filters would be available to your Controller Action on each request automatically, as the cookie would be sent along with every request. (This is of course also something to have in mind when considering which strategy to use. If you have alot of cookie data, this will slow down the perceived responsiveness of your application, as the cookie data has to be sent along with every request to your server. So keep your cookie data as small as possible)
On the other hand, if you expect the user to change the filters often and maybe even several times on the same page, you could consider using jQuery to do an asynchronous POST to your MVC controller, retrieve the neccessary data using JSON, and update the view. This is actually not as difficult as it might sound.
What you would need to do to implement it, is to create a Javascript function on your page that submits your parameters to your controller action. You can send the data as JSON to the controller action also. Something like this could work: (Untested code)
<script>
function submitFilters() {
var parameters = {
parameter1: $('#parameter1').val(),
parameter2: $('#parameter2').val(),
...
};
$.ajax('#Url.Action("SomeController", "SomeAction")', {
contentType: 'application/json',
data: JSON.stringify(parameters),
success: function(data) {
alert('Received data back from server. Ready to update the view.');
}
};
}
</script>
Then you would hook up the filters (Radio buttons, drop downs etc) to call the method submitFilters.
MVC automatically converts the JSON data it receives from the client into your C# ViewModel as long as the property names match. Then you can do whatever querying and filtering you need to on the server, and then send the data back as the result of the action. (Pseudo code)
public ActionResult SomeAction(MyViewModel vm)
{
var data = SomeRepository.GetData(vm.Parameter1, vm.Parameter2, ...);
return Json(data);
}
Then you just need to update the view when the data is received. You would preferably use a template engine for that, but it's not required of course.
This is just a crude example of how you could do it. Normally I would create a Knockout View Model to encapsulate it all. But this should get you started.

Custom Roles/Permissions in ASP.NET

I currently have a Web Application which is using it's own "Permissions" table which contains the following columns:
UserName - Windows UserName (Context.User.Identity.Name)
DivisionID - Links to a Division Table
RoleID - Comes from a custom Roles Table
RegionID - Recently added field to divide my Application into Countries (Canada, USA, International)
When the User logs into the site, they choose which Region they want to enter and I need to give them access to those Regions based on if they have any permissions set for that specific RegionID. Upon selecting a Region, the RegionID is stored in Session and will be used for this permission check and defining how data is populated on the pages (I haven't implemented the Session variable into all of the pages just yet so that can be changed if need be)
My initial thought would be to run my Permission Check on each page sending them to one of three destinations:
Invalid Permission Page (false)
Region Select Page - No Region selected in Session (RegionID = 0)
The page they requested - If has a permission set for that Region
I've also looked into using the Application_AuthenticateRequest method within the Global.asax but I cannot use Session within this area and it seems to be hitting the Application_AuthenticateRequest much more than it should be.
With my current App, what would be the best way to authenticate each user with their corresponding Regions, based on their Permissions?
I've really only worked with forms authentication-- but I'm assuming you'll be using windows authentication for membership and some form of custom roles authentication. I've never done it, but one would think it should work.
http://msdn.microsoft.com/en-us/library/system.web.security.roleprovider.getrolesforuser
You could create a custom provider that would take into account the Session value for Region in order to return the correct roles. I know for a web application, the default provider stores the roles as an encrypted cookie on the client. I'm thinking you can do something similar.
Normally I wouldn't recommend this method, but as it seems that you have already developed your application, you could relatively easily implement the following without too much upheaval:
Create a base class for your pages, and then inherit all the pages in your application from the base class. You would of course implement the "authorization" within the base class.
The one rather nasty problem with this is that if you forget to derive your page from the base class, then your page has no security on it.....but you could just as easily forget to implement your "Permission check"....
Something like
public class AuthorizedPage: System.Web.UI.Page
{
protected override void OnLoad(EventArgs e)
{
// ... authorization logic here...
// Be sure to call the base class's OnLoad method!
base.OnLoad(e);
}
}
You could check this out ASP.net "BasePage" class ideas and this https://web.archive.org/web/20211020133935/https://www.4guysfromrolla.com/articles/041305-1.aspx
Or, another idea, if you have used Master Pages you could also just do this stuff in the master page....

Global Variable inside of Controller MVC

So, I have this variable that I push into the controller via POST from a form in my view.
I then push the variable into viewdata so it's available to the next view which is fine. But that view has no need of a form so I'm unable to push that same variable into the next controller. In short, it's cumbersome to push pieces of information back and forth from controller to view and reverse, so I'm looking for a way to keep a global variable alive inside a controller so that it's accessible by all action results... The general breakdown of my program is this...
-User types a "name"
-I send "name" to controller.
-I push 'name' into viewstate (query entity framework to get a list of stuff 'name'
has access to) and return that list into the view.
-In that view I can access the 'name' since it was in view state.
-Clicking on a link inside the page takes me to another controller where I need
to get access to 'name' WITHOUT passing view Routing or POST.
Obviously the easiest way would be to declare 'name' globally and then it's always available but for the life of me I can't figure out how.
Have you considered storing it in the Session?
This will allow you to easily access it, either from your controller or views, and avoids the need for global variables.
Storing:
[HttpPost]
public ActionResult YourPostMethod(string name)
{
Session["Name"] = "yourName";
}
Access: *
Make Sure to check that it exists prior to grabbing it:
var whatsMyName = (Session["Name"] != null) ? Session["Name"] : "";
Scalability Consideration
It's worth mentioning that MVC applications are designed to mimic the web and are stateless. Introducing Session variables changes this, so be aware that it can introduce issues regarding scalability, etc.
Each user that stores data within the Session will take up resources at the server level. Depending on the number of users and what you are storing within the Session, you could potentially run out of memory if those values become too large.
Why not use the session object, which is an associative array which lives while the client is connected?
$_SESSION['name'] = "zzzz"; // store session data
name = $_SESSION['name']; //retrieve data
You can use this for each user till their session is active.. hope this helps

ASP.NET MVC 2 - Avoiding repeatedly querying the database for static data?

What can I do to avoid the repeated loading of static data every time a page is loaded in ASP.NET MVC2? This is pretty wasteful, and I would like to fix it.
My master page has the typical User Name + Company Name display in the top right corner. But because of the stateless nature of MVC, this data has to be looked up every single time a page is loaded, even though it never changes.
In the old webforms days I would just throw it into Session, but that seems to be discouraged in MVC. Plus the web app runs on a webfarm, so I really do not want to use session.
The web app already has quite a few static data items that get queried on every page load (user currency, user tax rate, etc), so I think a performance gain can be made by loading them only once at login time. I'm just not sure what the correct MVC method is (I am still quite new to MVC).
I always use the built-in ASP.NET cache for this, set the expiry appropriately and you're good to go.
public void Blah()
{
var company = HttpRuntime.Cache.Get("Company") as string;
if (company == null)
{
company = FetchCompanyFromDb();
HttpRuntime.Cache["Company"] = company;
}
return this.View(company);
}
Also see this previous question:
Caching in asp.net-mvc
Could you maybe encapsulate your User currency / User name / Tax etc into a user control and then use Output Caching on that?

ASP.NET MVC: How to handle cross-action TempData and ViewData

I'm trying to find a good way to handle the following scenario (I'm still kinda new to this):
A user can register through my site using an RPX/OpenId provider.
Step 1: The user authenticates through a provider. The provider returns a temporary token to one of my action methods.
Step 2: I use the token to grab the user's profile information and load a view which allows them to enter any missing required fields and optional fields.
I use 2 action methods in Step 2: One to handle the grabbing of information using the token. A second action which takes the authorization information and loads the missing/optional fields view.
I am passing the authorization info via TempData to the second action. The second action can handle validation so there's a chance I will need to hold on to the authorization object for more than just 1 request. I can't use the token to regenerate the authorization info because it's technically a one-use token, and it would be silly to regenerate the request since it is using network resources.
How could I persist the objects in my TempData for any subsequent requests to the same action, but remove the objects for any redirects? And since this may be a repeatable pattern in my application should I create a Filter to automatically handle this situation?
For example, I imagine a filter attribute which will merge TempData (if any) into ViewData - But how would I persist my data into future calls to the same action? Throw it into TempData again? And if I detect a redirect empty the TempData?
Thanks
I ended up re-adding the data to TempData if necessary. Since TempData does not allow you to add duplicate keys I created my own helper method to remove and then re-add the key:
public static void AddNew(this TempDataDictionary tempData, string key, object obj)
{
if ( tempData.ContainsKey( key ) ) tempData.Remove( key );
tempData.Add( key, obj );
}
I've had the same problem, but approached it a slightly different way - I've used a Forms Authentication method which works solely with OpenID... if my provider returns Authenticated, I do the following
var fields = openid.Response.GetExtension(typeof(ClaimsResponse)) as ClaimsResponse;
if (fields != null)
{
TempData["Email"] = fields.Email;
TempData["Nickname"] = fields.Nickname;
}
FormsAuthentication.RedirectFromLoginPage(openid.Response.ClaimedIdentifier, false);
break;
This means that I don't need to pass any kind of Authentication token around - after this code executes I know my user is authenticated.
Therefore, the action that this passes control to will simply copy the TempData fields, if populated, into ViewData and pass them onto the view.
Validation is handled after this - I don't care what comes back from OpenID (i.e. is it valid or not), I let the user edit this and then save, and then perform my validation.

Categories