ASP.NET 4.6 MVC Output Caching with Razor Syntax Issues - asp.net

I have an ASP.NET 4.6 project that uses MVC controllers that return my views and WebAPI Controllers that return data.
I want to add output caching to my MVC controllers because I've done a few tests and the performance difference on page load is massive.
[OutputCache(CacheProfile = "PageCache")]
public ActionResult Home()
{
return View("~/Views/Home/Index.cshtml");
}
The only challenge I'm facing is that the razor syntax on the views is also cached.
For example if user 1 loads a route, user 2 will get the same route with the username injected from user 1.
Besides getting rid of all the razor syntax in the pages, what are my options? Does anyone have a good solution to this problem?

You can use VaryByCustom or VaryByParam attributes. The following code is the cache based on the parameter 'id'.
[OutputCache(Duration = int.MaxValue, VaryByParam = "id")]
public ActionResult Details(int id)
{
ViewData.Model = _dataContext.Movies.SingleOrDefault(m => m.Id == id);
return View();
}
You can create a cache profile also in the web.config file
<caching>
<outputCacheSettings>
<outputCacheProfiles>
<add name="Cache1Hour" duration="3600" varyByParam="none"/>
</outputCacheProfiles>
</outputCacheSettings>
</caching>
For more info check this Link

You should not cache user information on the server, it should be cached on the client.
Please take a look at this document.
https://learn.microsoft.com/en-us/aspnet/mvc/overview/older-versions-1/controllers-and-routing/improving-performance-with-output-caching-cs

Related

Alternative to Server.Transfer in ASP.NET Core

I am migrating an ASP.NET application to ASP.NET Core and they have some calls to HttpServerUtility.Transfer(string path). However, HttpServerUtility does not exist in ASP.NET Core.
Is there an alternative that I can use? Or is Response.Redirect the only option I have?
I want to maintain the same behaviour as the old application as much as possible since there is a difference in between Server.Transfer and Response.Redirect.
I see some options for you, depending on your case:
Returning another View: So just the HTML. See answer of Muqeet Khan
Returning another method of the same controller: This allows also the execution of the business logic of the other action. Just write something like return MyOtherAction("foo", "bar").
Returning an action of another controller: See the answer of Ron C. I am a bit in troubles with this solution since it omits the whole middleware which contains like 90% of the logic of ASP.NET Core (like security, cookies, compression, ...).
Routing style middleware: Adding a middleware similar to what routing does. In this case your decision logic needs to be evaluated there.
Late re-running of the middleware stack: You essentially need to re-run a big part of the stack. I believe it is possible, but have not seen a solution yet. I have seen a presentation of Damian Edwards (PM for ASP.NET Core) where he hosted ASP.NET Core without Kestrel/TCPIP usage just for rendering HTML locally in a browser. That you could do. But that is a lot of overload.
A word of advice: Transfer is dead ;). Differences like that is the reason for ASP.NET Core existence and performance improvements. That is bad for migration but good for the overall platform.
You are correct. Server.Transfer and Server.Redirect are quite different. Server.Transfer executes a new page and returns it's results to the browser but does not inform the browser that it returned a different page. So in such a case the browser url will show the original url requested but the contents will come from some other page. This is quite different than doing a Server.Redirect which will instruct the browser to request the new page. In such a case the url displayed in the browser will change to show the new url.
To do the equivalent of a Server.Transfer in Asp.Net Core, you need to update the Request.Path and Request.QueryString properties to point to the url you want to transfer to and you need to instantiate the controller that handles that url and call it's action method. I have provided full code below to illustrate this.
page1.html
<html>
<body>
<h1>Page 1</h1>
</body>
</html>
page2.html
<html>
<body>
<h1>Page 2</h1>
</body>
</html>
ExampleTransferController.cs
using Microsoft.AspNetCore.Diagnostics;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
namespace App.Web.Controllers {
public class ExampleTransferController: Controller {
public ExampleTransferController() {
}
[Route("/example-transfer/page1")]
public IActionResult Page1() {
bool condition = true;
if(condition) {
//Store the original url in the HttpContext items
//so that it's available to the app.
string originalUrl = $"{HttpContext.Request.Scheme}://{HttpContext.Request.Host}{HttpContext.Request.Path}{HttpContext.Request.QueryString}";
HttpContext.Items.Add("OriginalUrl", originalUrl);
//Modify the request to indicate the url we want to transfer to
string newPath = "/example-transfer/page2";
string newQueryString = "";
HttpContext.Request.Path = newPath;
HttpContext.Request.QueryString = new QueryString(newQueryString);
//Now call the action method for that new url
//Note that instantiating the controller for the new action method
//isn't necessary if the action method is on the same controller as
//the action method for the original request but
//I do it here just for illustration since often the action method you
//may want to call will be on a different controller.
var controller = new ExampleTransferController();
controller.ControllerContext = new ControllerContext(this.ControllerContext);
return controller.Page2();
}
return View();
}
[Route("/example-transfer/page2")]
public IActionResult Page2() {
string originalUrl = HttpContext.Items["OriginalUrl"] as string;
bool requestWasTransfered = (originalUrl != null);
return View();
}
}
}
Placing the original url in HttpContext.Items["OriginalUrl"] isn't strictly necessary but doing so makes it easy for the end page to know if it's responding to a transfer and if so what the original url was.
I can see this is a fairly old thread. I don't know when URL Rewriting was added to .Net Core but the answer is to rewrite the URL in the middleware, it's not a redirect, does not return to the server, does not change the url in the browser address bar, but does change the route.
resources:
https://weblog.west-wind.com/posts/2020/Mar/13/Back-to-Basics-Rewriting-a-URL-in-ASPNET-Core
https://learn.microsoft.com/en-us/aspnet/core/fundamentals/url-rewriting?view=aspnetcore-5.0
I believe you are looking for a "named view" return in MVC. Like so,
[HttpPost]
public ActionResult Index(string Name)
{
ViewBag.Message = "Some message";
//Like Server.Transfer() in Asp.Net WebForm
return View("MyIndex");
}
The above will return that particular view. If you have a condition that governs the view details you can do that too.
I know that this is a very old question, but if someone uses Razor Pages and is looking to a Server.Transfer alternative (or a way to return a different view depending on a business rule), you can use partial views.
In this example, my viewmodel has a property called "UseAlternateView":
public class TestModel : PageModel
{
public bool UseAlternateView { get; set; }
public void OnGet()
{
// Here goes code that can set UseAlternateView=true in certain conditions
}
}
In my Razor View, I renderize a diferent partial view depending of the value of the UseAlternateView property:
#model MyProject.Pages.TestModel
#if (Model.UseAlternateView)
{
await Html.RenderPartialAsync("_View1", Model);
}
else
{
await Html.RenderPartialAsync("_View2", Model);
}
The partial views (files "_View1.cshtml" and "_View2.cshtml"), contain code like this:
#model MyProject.Pages.TestModel
<div>
Here goes page content, including forms with binding to Model properties
when necessary
</div>
Obs.: when using partial views like this, you cannot use #Region, so you may need to look for an anternative for inserting scripts and styles in the correct place on the master page.

How to re-implement legacy aspx with ServiceStack and maintain the address?

Is it possible to keep the following address and re-implement it with ServiceStack?
http://example.com/Routing/LeadPost.aspx?LeadType=AAA&MYId=3000
I don't have access to the original code as it was a 3rd party who created it, but I do know what the post to it, a gigantic xml package. This I have mimicked perfectly with a clean request DTO and service, but I'm not clear on the addressing part, or if it's even plausible. Below is my implementation so far.
public class Service : ServiceStack.Service
{
public IMessageQueueClient MessageQueueClient { get; set; }
public object Post(LeadInformation request)
{
if (request == null) throw new ArgumentNullException("request");
var sw = Stopwatch.StartNew();
MessageQueueClient.Publish(request);
return new LeadInformationResponse
{
TimeTakenMs = sw.ElapsedMilliseconds,
};
}
}
Thank you,
Stephen
You can use this route definition to handle the Legacy ASP.NET WebForms Request:
[Route("/Routing/LeadPost.aspx")]
public class LegacyLeadPost
{
public string LeadType { get; set; }
public int MyId { get; set; }
}
Which will let you handle the desired route with LeadType and MyId properties populated:
/Routing/LeadPost.aspx?LeadType=AAA&MYId=3000
An alternative approach is to use a WebForms Page and call into ServiceStack, the ServiceStack Integration docs explores the different ways from accessing ServiceStack from external ASP.NET MVC or WebForms Web Frameworks.
When you installed ServiceStack (at least up to version 3, which is what I last used), it adds something to your application's Web.Config file that looks like:
<system.web>
<httpHandlers>
<add path="somePath/*" type="ServiceStack.WebHost.Endpoints.ServiceStackHttpHandlerFactory, ServiceStack" verb="*" />
</httpHandlers>
</system.web>
If you modify the path attribute to match your legacy URL, it may work. Then again, without knowing what other HttpHandlers are set up for your application, it is impossible to tell for sure. Also keep in mind that you may introduce conflicts, because there is an HttpHandler that passes all .aspx urls to the .NET webforms Page base class and all the other code-behind files in your application.

ASP.NET MVC 3 caching or not caching action strategy

I have an action, let's say /Foo/Bar with a GET parameter in this action,
get_cached, who define if we want to get the cached value or the "realtime".
This is made with the following code :
public ActionResult Bar()
{
var useCache = Request.Params["get_cached"] == "1" ? true : false;
if (useCache)
{
return RedirectToAction("BarCached");
}
else
{
return RedirectToAction("BarRealTime");
}
}
[OutputCache(Duration = 100, VaryByParam = "*")]
public ActionResult BarCached()
{
return Content("mystuff_cached");
}
public ActionResult BarRealTime()
{
return Content("mystuff_realtime");
}
No problem with this code, apart the url will be shown as BarCached or BarRealTime and i would get only Bar (the main action name).
I tried to change the RedirectToAction to the full method name like this :
return this.BarCached()
But this disable the cache capabilities !
So, how can render the ActionResult code from a method (render BarCached from Bar) using the OutputCache definitions on this method (OutputCache on BarCached) ?
Thanks by advance.
In the asp.net pipeline, ResolveRequestCache (which OutputCache relies on) occurs just after the request is authenticated. In your example above, by the time you have gotten to "Bar" it's too late to use output caching, as you have noted by saying that this.BarCached() doesn't recognize the cache attribute.
If your problem is the performance of whatever generates "mystuff_", could you not just save the result of that call to the application cache and return it in your Bar() method instead of the RedirectToAction objects?
Not much of a solution I know, but hopefully helpful just the same.
I ended using the System.Web.Caching namespace who is the base cache handler of asp.net MVC.
I can access the cache repository of Asp.NET MVC with System.Web.HttpContext.Current.Cache
Using that, I store the ActionResult of "BarCached" and then I can get the cache feature the way I want using something like this :
Add a value to the cache
System.Web.HttpContext.Current.Cache.Insert(
"mykey",
"myvalue",
null,
DateTime.Now.AddSeconds(expirationInSeconds),
System.Web.Caching.Cache.NoSlidingExpiration
);
And get value from the cache
var myvalue = System.Web.HttpContext.Current.Cache.Get("mykey")

Elegant way of avoiding ASP.NET MVC routing errors

I have a route:
routes.MapRoute("ResetPasswordConfirm", "reset-password-confirm", new { controller = "Membership", action = "ResetPasswordConfirm" });
and the code
public ActionResult ResetPasswordConfirm(int userid, string key)
{
// ...
}
in my application. So that i have url to be executed like this:
http://localhost/reset-password-confirm?userid=1&key=bla_bla_something
That is absolutely okay, until someone decides to go to
http://localhost/reset-password-confirm
...and look what will happen. ASP.NET will generate predictable error:
The parameters dictionary contains a null entry for parameter 'userid' of non-nullable type 'System.Int32'...
It could be done also by a search robot trying to grab all the possible urls. It's okay, but generates a lot of errors during usage of application in the wild. I want to avoid that, but feel uncomfortable with writing a stub for every possible case for such kind of errors.
Is there any elegant way of doing that? Thanks.
Another way is to handle global errors, just set <customErrors mode="On"></customErrors> on your web.config and create an Error.cshtml on your Shared view folder. MVC3 templates actually include that page.
On the other hand, if you want to be more specific, you should try Action Filters, that's a cool way to handle errors.
[HandleError(View = "YourErrorView", ExceptionType=typeof(NullReferenceException))]
public ActionResult ResetPasswordConfirm(int? userid, string key)
{
if (!userid.HasValue)
throw new NullReferenceException();
// ...
}
Use nullables for your parameters, i.e.:
public ActionResult ResetPasswordConfirm(int? userid, string key)

Correct way of adding Page Method to ASP MVC project?

I'm just getting started with ASP.NET and the MVC model. I've worked my around the basic concepts and have a few models, controllers, and views working fine. I know how to add Web Methods and Page Methods to a Web Service, but cannot figure it out for MVC projects.
I (think) I need to add a Page Method to my project as the correct way of responding to AJAX requests. This is what my controller looks like:
namespace MyProject
{
public class OrderController : Controller
{
public ActionResult Place (ProductSku sku)
{
var order = Order.NewOrder(sku);
var db = new SystemDiscsLib.Database();
db.SaveOrder(order);
return View(order);
}
[WebMethod]
public static string GetDate ()
{
return DateTime.Now.ToString();
}
POSTing to /Order/Place works fine, and the view is created displaying the contents of Views/Order/Place.aspx and everyone is happy. However, any requests made to /Order/GetDate fail with a The resource cannot be found error. I (think) I have correctly enabled Page Methods by adding this to my Web.config, under system.web:
<httpModules>
<add name="ScriptModule" type="System.Web.Handlers.ScriptModule, System.Web.Extensions, Version=1.0.61025.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
</httpModules>
I do not have a view called GetDate as I don't want a view, I'll just be returning JSON data. I intend to use JQuery, so I didn't do the EnablePageMethods and ScripManager stuff, per this article.
PageMethods are for WebForms, not MVC. Change your method to look more like this:
public JsonResult GetDate()
{
return Json(DateTime.Now.ToString());
}
Note that the method is no longer static, and you should not use the [WebMethod] attribute.

Resources