OutputCache and recording unique views? - asp.net-mvc

Image i have a view that is cached with the OutputCache attribute but i still need to increment a counter that records that the page has been viewed, how could i do it?
I thought about creating my own Custom ActionFilterAttribute, and using Action Filter Order of Execution to record this .. but i'm not sure it will work.
eg.
[IncrementViewCountFilter(Order=1)]
[OutputCache(Duration=60,Order=2)]
public ActionResult Index(int questionId)
{ ... }
Firstly, my assumption here is that if the OutputCache is called, and the page is cached, then the controller code will not be ran.
Next problem i'm guessing is that the IncrementViewCountFilter wouldn't know about the questionId, so how would it know what to increment (because it is executed before the main Index code is executed).
Secondly, if the IncrementViewCountFilter did know the questionId .. and it's getting lots of hits, you wouldn't want it to write all the time to the DB.. but only when it gets to a certain number .. and then u 'flush' the output.
Anyone have any thoughts?

Well, you have a few options.
Donut caching
One server-side option is 'Donut caching'. Donut caching allows most of the page to be cached, and portions of the page to be not cached (the hole in the middle of the donut). Donut caching is described here, and I have used it with great success.
Image-based tracker
Another option is having an image on the page actually load a server-side action that records the hit. This would look like
<img src="/controller/action">
on the page, where the action serves up an empty image at the end.
Client-side tracking
The last option is client-side tracking -- where some script runs on the client side and uses AJAX to call something on the server to record the hit. Google uses something like this for their Analytics package. If you're on the same domain as your tracking mechanism ... like if your main page is:
http://www.domain.com/home/action
and the tracker is on
http://www.domain.com/tracking/action
then you should be fine.
This gets tricky when your tracker is on a different domain (you need to handle this using JSONP or some other mechanism that allows for relatively safe cross-site scripting).

The filter can get the questionId from the ActionExecutingContext.ActionParameters, which is passed to OnActionExecuting. As for caching the hit counts, well, use the cache. :)

You could also use an HttpModule which is a good option because it can be used for pages and other assets that do not use the MVC pipeline. I use a combination of Donut caching,(http://mvcdonutcaching.codeplex.com/), an MVC filter and an HttpModule to record all types of analytics for cached pages.

I don't know about the MVC side but if I was doing this in WebForms this sounds like it would be a candidate for output cache substitution aka donut caching.

Related

Proper way to remember multiple parameters across requests in Rails

My application feature a "main" page where most of the action happens: There are tags for filtering and a list of results in a (paginated) table, plus the possibility to select some or all results in a "shopping cart".
This page has to keep track of a whole lot of things: what tags are selected, what items are selected, and how the result table is sorted and what page it's on. Everything has to persist, so if I select a new tag, the page must partially reload but remember everything (sorting, what's selected).
Right now I'm handling everything with parameters, and for each action taken on the page, all links (select a tag/item, change page, sort table) are updated to include previous parameters + the relevant new addition. This works, obviously, but it feels kind of inefficient, as I have to reload more of the page than I want to. How is this situation normally handled? I can't find that much info on google at all, but it doesn't feel like a particularly uncommon case.
tl;dr: How to best make sure all links (to the same page) always include everything previously selected + the new action. There are a lot of links (one per tag to select/deselect, one per result item to select/deselect, one per sort option, one per page)
There are five ways to do that:
Method 1: By parameters
You mentioned this. I never think of this as it's too troublesome. Anyway it's still a solution for very simple case.
Method 2: By cookie
Save the settings to a cookie and read the cookie in controller to arrange layout settings.
Method 3: By LocalStorage
Similar to cookie but allows more space.
Method 4: By Session
If you are using ActiveRecord to save session, this could be the best solution for pure pages loading. Save the user preferences into session and load it in next layout.
Method 5: Use Ajax
This is the best solution IMO. Instead of whole page loading, use Ajax to refresh/retrieve changes you need. Using together with above method, a user can even continue his last preferences. This is the most powerful and should be applicable to your case which looks like a web app than a website.
Have you tried creating model for all those attributes? and just always load the 'latest' when on the page load, if you dont need them you can always have a flag for that session.

Outputcache override with query string showing non cache version in asp.net mvc

Id like to show a page non cached, but still take advantage of outputcache. Say a admin would like to see the page updated in real time by adding a query string nocache=1 to the url, then the outputcache wouldnt show the cached version, but if the query string is left off it would. Can I do this?
The best alternative would probably be to implement a custom cache provider which, when given a certain set of parameters, does not cache the page. Here is one such example:
http://www.haneycodes.net/custom-output-caching-with-mvc3-and-net-4-0-done-right/
If you create some miscellaneous parameter and configure the output caching to varyByParam, you should be able to call it with different values each time, it should bypass the cache... However I'm not sure this would be the best way to do that. You may want to just create a separate action for the admin to access that is secured.

Maintain parameter info in the request path for all pages instead of the subdomain

I seek some guidedence here ... ( I'm not sure if this is the best title )
At the moment I prepend a "server name" to the url like this:
server10.example.com
This works fine, except that I need to handle all the subdomains on the IIS and I'm not sure google are happy about jumping around from sub to sub to sub, when it seems the links to the other servers.
I'm kind a hoping for a nice way to archive this wioth asp.net mvc.
Most pages are related to a "server" ... there are however a few info pages, contact, home that dont really need a valid "server" name ... but could just be "na" for not available, but the name need to be maintained, if there is already a selected server, when a user are keeps browsing the site. This needs to be as transparent as possible when I need to create the links to the diffenrent pages.
I could extend the Html Action() extensien to automatically add the selected "server" from the previusly request to the page.
In the format:
/{serverParameter}/{controller}/{action}/{parameterInfo}
And if no server is selected, just add "na" as the {server} placeholder.
I'm not sure if more information is needed, but please let me know if ...
I tired of extracting the selected server from the domain part and the other way also seems better, I just can't think of a good way to structure this ...
Updated
90% of all the pages are about a server that the user select at some point. Could be server10, server9, server20 ... just a name. I want to maintain that information across all pages, after the users has selected it or else I just want it to be f.ex: "empty".
I mostly looking for an easy way of doing this or an alternative ... atm I'm prepending the serverParamter to the url so it ends up being: "serverParameter.example.com".
I want to end up with something like
http://example.com/{server}/{controller}/{action}
instread of
http://{server}.example.com/{controller}/{action}
If I understand your question correctly, you just wish to group different collections of content together above the controller/action level. If that's the case, have you considered using ASP.NET MVC areas?
Just right-click on your project, and choose Add -> Area.... Give it a name (what you're calling "server"), and then you can add content, your own controllers, actions, etc. Under this area. You will automatically be able to access it via /AreaName/Controller/Action/etc.
I went with the already impemented routing in ASP.NET MVC.
{server}/{controller}/{action}
When creating the links it takes the set value for {server} and places the value when generating URL's, so I only need to supply controller and action in the #Html.Action helper method ... this could not have been more easy.
I'm not sure why I did not think about this. One just gotta love routing.

.Net MVC: Set Model property from Javascript

So this seems like it should be simple. I'd like to set a public model property from javascript that's fired on the mouseover of an image.
So something like this:
Html:
...img src="<%=Model.AppDetails.Logo%>" onmouseover="showMenu(this);...
Javascript:
function showMenu(app) {
<%Model.CurrentId = app.id%> //app.id is of course undefined here
...
Or is a better approach to call a controller method to set it? If so what's the best approach? Ajax? Seems like a lot of heavy lifting for something so simple.
Appreciate the feedback.
Sharon
What exactly are you trying to do with Model.CurrentId? If you're trying to just send it back to the server when the page next sends information, use a hidden field or similar.
However, if you're trying to set a value on the server when the user mouses over an image, of course you need to use ajax or similar. How else is the server going to know what the client is doing.
If you want to use Model.CurrentId later in the same file, you will need to re-apply the template as well. Once the view is processed on the server, the client has no information from the template (the client doesn't see the <%...%> tags, those are replaced on the server before sending).
I can't imagine any scenario where you'd want to send information to the server on a mouseover unless you were going to also get information back (like an extended tooltip, or an image popup), so ajax is probably your best option.

How do I make a "back" link?

I have a detail page that gets called from various places and has a nice readable url like
"www.mypage.com/product/best-product-ever".
The calling pages (list of products) have a more complex url like:
"www.mypage.com/offers/category/electronic/page/1/filter/manufacturer/sony/sort/price" and
"www.mypage.com/bestseller/this-week".
How can I make a backlink from the detailpage to the calling product list?
I cannot use javascript
I don't want to have the calling page in the URL, because it gets to long
I really want links between pages, no http-post
I cannot use Sessionstate
EDIT:
Sessionstate is ruled out, because if there are 2 Windows open, then they would share the same "Back" page information.
Like Lee said, use the referrer value:
Back
If you don't want the URL in the link because it's too long, try running some sort of simple compression algorithm over the URL, display the compressed data as unicode text and then append the compressed URL as a parameter to a redirect page, e.g:
Back
What about using the referrer header value?
Here's a crazy idea that will require a fair but of work and may not be healthy for performance (depending on your users).. but here we go:
Create a repository for caching 'ListResults' (and wire it to persist to the DB of you like.. or just leave it in memory on the server).
In short what this Repo can do is store a ListResult which will include everything to persist the state of the current view of the list any given user is looking at. This might include routes and other values.. but essentially everything that is needed to redirect back to that specific page of the filtered and sorted list.
As the ListResult item is added to the repo a small unique hash/key is generated that will be url friendly - something like this "k29shjk4" - it is added to the item along with a datetime stamp.
ListResults are only persisted from the moment a list gets off the default view (ie. no filtering, sorting and Page 1) - this will help in a small way for performance.
A ListResult item may never actually get used but all detail actionlinks on the particular list view have the ListResult.Key hash value added to the route. So yes, it might end up as a querystring but it will be short (url friendly) and if you wanna mess with routes more, you can tidy it up further.
For navigation "back" to the list, you may need a new small controller which accepts simply the ListResult.Key hash value and redirects/re-creates the state of the list view (paging, filtering and sorting included) from the lookup in the repo.
So we have met the requirements so far: no calling page in the url (in the sense that its not the whole page - just a hash lookup of it); no POSTing, no sessions, no js.
To stop the ListResult repo from getting to big (and dangerous: if you persist it to the DB), you can use a ASP.NET background service to periodically prune the 'old' routes by way of the timestamp.. and 'extend' the life of routes that are continuously being used by adding time to the stamp of a ListResult item when it's requested via the new controller. No need to persist a route indefinitely coz if a user wants a permalink to a list view, they can bookmark the long list route itself.
hope this helps somehow
Do you have a cookie?
If so, you can put it in there, or use it to create your own session state.
I think this is more like a "Back to results" then a generic "<< back" link, because you would expect the generic back link to return to the genetic list, not the heavily filtered list you described, right?
I don't know if this falls into your "no post" condition, but the only option I can see is having the Detail action be POST-only ([AcceptVerbs(HttpVerbs.Post)]) and include another parameter like string fullRoute which is converted to the 'link' on the detail page for "Back to results". Overload the Detail action missing the fullRoute param and have the overloaded action be a GET action so that the POST fullRoute value is not required (for when users are ok with the 'generic' "Back" link). This should serve both 'generic' GET requests to the Detail page and the POST request which will include the specific "Back to results" link for the filtered list.

Resources