The image of a login page (clickable image):
Is it seems (though the debug viewer), the ViewEngines are both (razor+webforms, im using the first) there, in the ViewEngines collection, and them both can find the views (manually) and display them, also tried manually.
So why is that they cannot do it on their own? This is simply an action method, nothing more:
public ViewResult Login()
{
return View();
}
Major Update
I have overridden the OnResultExecuting method:
protected override void OnResultExecuting(ResultExecutingContext filterContext)
{
if (filterContext.Result is ViewResult)
{
if (((ViewResult)filterContext.Result).ViewEngineCollection.Count == 0)
{
((ViewResult)filterContext.Result).ViewEngineCollection.Add(new RazorViewEngine());
}
}
base.OnResultExecuting(filterContext);
}
And it turned out that every time and every view I do now need to add new IViewEngine to a ViewResult. Now why is that so? Even if the ViewEngines.Engines collection is not empty?
Minor update.
The ViewEngineCollection stopped being empty at some point of idling.
Do you have a master page defined?
If you have a _ViewStart.chstml file in the root of your solution then it is possible the engine is trying to load a master page to combine with your view.
To skip the master page tell your view to not use a master page. Add the following to the View
#{
Layout = null;
}
or make sure that the master page referenced in the _viewStart.cshtml file exists.
You may not have a correct ViewEngines configured (or none).
Try adding to view engines manually to identify if there is a configuration error somewhere.
Add something like this to Application_Start() method of Global.asax.
protected void Application_Start()
{
ViewEngines.Engines.Clear();
ViewEngines.Engines.Add(new RazorViewEngine());
// Other code
}
This should initialise the Razor view engine, and if as a result mvc starts searching locations, you have somehow turned off razor in the configuration.
The ViewEngineCollection stopped being empty at some point of idling.
This only might refer to some ASP.NET caching of old assemblies which was not for some reason updated by the new ones. This happens from time to time, and I have to clear the cache in order for some new code to work.
Related
I'm using ASP.NET MVC in Visual Studio 2015. The app has the following structure:
MyApp
Controllers
Controller1
Actions
Create
Delete
Details
Edit
IndexPartial
Controller2
Actions
Edit
Controller3
Actions
Edit
Views
Controller1
Create
Delete
Details
Edit
IndexPartial
Controller2
Edit
Controller3
Edit
The app displays Controller1/IndexPartial view on the Controller2/Edit view and on Controller3/Edit. This partial view displays rows of data, each with Edit, Details, Delete buttons which take the user to the Controller1 views for those actions.
When the user is done with the Controller1 action, they need to return to Controller2/Edit or Controller3/Edit via the Back to List button or when the Save/Delete buttons are clicked. But how do we determine where the user originated? Did the user come from the Edit of Controller2 or Controller3?
We've thought of using a session variable. Can RouteConfig.cs be used to track the user's path and help determine where s/he should return? How do we do this via routes in MVC?
Thank you for your help.
Update: This is all done via the server; no JavaScript (Angular, etc.).
The routing engine has nothing to do with what you need. You need to track user navigation and a good way to do this is using ActionFilters.
You can create a custom ActionFilter that checks the UrlReferrer on its OnActionExecuted and decides how to redirect the request to the appropriate Controller/Action.
[Example]
ActionFilter
public class RedirectAfterActionFilter : ActionFilterAttribute, IActionFilter
{
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
// Your decision logic
if (filterContext.HttpContext.Request.UrlReferrer.AbsolutePath == "something usefull")
{
filterContext.Result = new RedirectToRouteResult("Your Route Name", routeValues: null); // redirect to Home
}
base.OnActionExecuted(filterContext);
}
}
ActionFilter usage
[RedirectAfterActionFilter]
public ActionResult DoSomethingAndGetRedirected()
{
// Save, Edit or Whatever
//...
return new EmptyResult(); // no need to return since the user will be redirected by the filter
}
Extra: Read How to redirect from a action filter if you dislike to use Route names to redirect.
There are two aspects to this:
The "Back to List" link
The "Save/Delete" actions
As far as the "Back to List" link, your controller should be giving the view all the information it needs to produce a viable GUI. Pass an identifier (or even the actual return URL) to the view in the ViewBag as a dynamic property and let the view render the link to the destination.
For the "Save/Delete" actions, it depends on how they are implemented.
If it's all JS with http requests then the same concept above applies.
If you are posting back to the server however, the controller will have to do the redirection with something like RedirectToAction().
How about storing the previous location in a ViewBag and then populate your button href with the ViewBag content...
Or
You can use Url Referrer, which fectches the previous url that linked to current page.
Of course the best method will depend on your implementantion, without seeing your code those two are the best option that I can think of.
I would like to display the view in the folder if no controller/action matches.
For example www.site.com/Home/Index, if I have the normal default route {controller}/{action}/{id} then I need a HomeController with method Index.
And there is a folder in Views folder called Home and the file Index.cshtml
If i try www.site.com/About/Index i need to create the AboutController and the method index.
But I have just the folder About and file Index.cshtml.
I would like that if the default route does not match but I have a Folder and a File in the Views folder that match the patern: {controller} is the folder {action} is the view; then that view is displayed.
How could I achive that?
For missing actions you can override HandleUnknownAction.
For missing controllers you can implement a custom DefaultControllerFactory and override GetControllerInstance with something like this:
protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType) {
if (controllerType == null)
return new DumbController();
return base.GetControllerInstance(requestContext, controllerType);
}
class DumbController : Controller {
protected override void HandleUnknownAction(string actionName) {
try {
View(actionName).ExecuteResult(this.ControllerContext);
} catch (Exception ex) {
throw new HttpException(404, "Not Found", ex);
}
}
}
I came across the same issue recently while developing AJAX web applications where most of the pages don't actually need a controller (all the data is returned via Web API calls).
It seemed inefficient to have dozens of controllers all with a single action returning the view so I developed the ControllerLess plugin which includes a default view controller behind the scenes with a single action.
If you create a controller for your view, then MVC will use that. However, if you create a view without a controller, the request is re-routed via the default plugin controller.
It works with C# and VB.NET and us available at https://www.nuget.org/packages/ControllerLess
The source code is also available on GitHub at https://github.com/brentj73/ControllerLess
You cannot and shouldn't the way you want. Views cannot be addressed by design (in the web.config in the /views folder theres an HttpNotFoundHandler mapped to * to ensure this)
With that said, what you want here is not really standard so why do you want to do this, maybe we can come up with a better suggestion based on the reason behind this?
Never tried this, but here's a thought. You can setup constraints for the routes, and thus you should be able to create a route matching "{folder}/{file}" where you constraint them to valid values (you can google this, or seach her on SO), and set it to run on a FileController (arbitrary name) with some default action. Then, in that action, simply return the desired view. Something like:
public class FileController : Controller {
public ActionResult Default(string folder, string file) {
return View(folder + "/" + file);
}
}
For my blog I am wanting to use the Output Cache to save a cached version of a perticular post for around 10 minutes, and thats fine...
<%#OutputCache Duration="600" VaryByParam="*" %>
However, if someone posts a comment, I want to clear the cache so that the page is refreshed and the comment can be seen.
How do I do this in ASP.Net C#?
I've found the answer I was looking for:
HttpResponse.RemoveOutputCacheItem("/caching/CacheForever.aspx");
The above are fine if you know what pages you want to clear the cache for. In my instance (ASP.NET MVC) I referenced the same data from all over. Therefore, when I did a [save] I wanted to clear cache site wide. This is what worked for me: http://aspalliance.com/668
This is done in the context of an OnActionExecuting filter. It could just as easily be done by overriding OnActionExecuting in a BaseController or something.
HttpContextBase httpContext = filterContext.HttpContext;
httpContext.Response.AddCacheItemDependency("Pages");
Setup:
protected void Application_Start()
{
HttpRuntime.Cache.Insert("Pages", DateTime.Now);
}
Minor Tweak:
I have a helper which adds "flash messages" (Error messages, success messages - "This item has been successfully saved", etc). In order to avoid the flash message from showing up on every subsequent GET, I had to invalidate after writing the flash message.
Clearing Cache:
HttpRuntime.Cache.Insert("Pages", DateTime.Now);
Hope this helps.
Using Response.AddCacheItemDependency to clear all outputcaches.
public class Page : System.Web.UI.Page
{
protected override void OnLoad(EventArgs e)
{
try
{
string cacheKey = "cacheKey";
object cache = HttpContext.Current.Cache[cacheKey];
if (cache == null)
{
HttpContext.Current.Cache[cacheKey] = DateTime.UtcNow.ToString();
}
Response.AddCacheItemDependency(cacheKey);
}
catch (Exception ex)
{
throw new SystemException(ex.Message);
}
base.OnLoad(e);
}
}
// Clear All OutPutCache Method
public void ClearAllOutPutCache()
{
string cacheKey = "cacheKey";
HttpContext.Cache.Remove(cacheKey);
}
This is also can be used in ASP.NET MVC's OutputCachedPage.
On the master page load event, please write the following:
Response.Cache.SetExpires(DateTime.UtcNow.AddMinutes(-1));
Response.Cache.SetCacheability(HttpCacheability.NoCache);
Response.Cache.SetNoStore();
and in the logout button click:
Session.Abandon();
Session.Clear();
Hmm. You can specify a VaryByCustom attribute on the OutputCache item. The value of this is passed as a parameter to the GetVaryByCustomString method that you can implement in global.asax. The value returned by this method is used as an index into the cached items - if you return the number of comments on the page, for instance, each time a comment is added a new page will be cached.
The caveat to this is that this does not actually clear the cache. If a blog entry gets heavy comment usage, your cache could explode in size with this method.
Alternatively, you could implement the non-changeable bits of the page (the navigation, ads, the actual blog entry) as user controls and implement partial page caching on each of those user controls.
If you change "*" to just the parameters the cache should vary on (PostID?) you can do something like this:
//add dependency
string key = "post.aspx?id=" + PostID.ToString();
Cache[key] = new object();
Response.AddCacheItemDependency(key);
and when someone adds a comment...
Cache.Remove(key);
I guess this would work even with VaryByParam *, since all requests would be tied to the same cache dependency.
why not use the sqlcachedependency on the posts table?
sqlcachedependency msdn
This way your not implementing custom cache clearing code and simply refreshing the cache as the content changes in the db?
HttpRuntime.Close() .. I try all method and this is the only that work for me
I'm trying to get the master page changed for all my aspx pages. For some reason I'm unable to detect when this function is called for a ascx page instead. Any help in correting this would be appreciated.
protected override void OnActionExecuted(ActionExecutedContext filterContext)
{
var action = filterContext.Result as ViewResult;
if (action != null && action.MasterName != "" && Request.IsAjaxRequest())
{
action.MasterName = "Ajax";
}
base.OnActionExecuted(filterContext);
}
If you're still keen on changing the master page based on the fact whether your request is ajax or not - I just accidentally stumbled on exactly the thing you were looking for:
http://devlicio.us/blogs/sergio_pereira/archive/2008/12/05/reusing-views-in-asp-net-mvc-for-ajax-updates.aspx
Basically, instead of overriging the OnActionExecuting method in the BaseController - override the View method! You get exactly the thing you want, with a method that seems specifically designed for it :)
protected override ViewResult View(string viewName, string masterName, object model)
{
return base.View(viewName, Request.IsAjaxRequest() ? "Empty" : masterName, model);
}
So you're saying that MasterPage is empty when you're executing actions to .ascx "pages"?
.ascx's aren't pages, they're UserControls / PartialViews. And as such they don't have master pages. They can be dropped in a mage, or a master page.. But if your request is returning an .ascx, it won't have a master page.. )
UPD:
This is most likely because of the way MVC works - all the 3 parts (M-V-C) are completely independant. Which means that when your code executes inside the controller, we know nothing at all about the view. And the View is the one that selects the master page, not the controller.
Tbh if you're trying to change the way the app looks (change the master page) inside the controller - you're most likely doing something wrong. It was designed with separation of context in the first place, and you're trying to go around it :)
UPD2:
So you're saying that you want to return the full page + master page for regular requests, and just the page without the master (well, clean at least) for ajax requests? You're still trying the wrong approach.
Here's what I've been doing instead:
if (!Request.IsAjaxRequest())
return View(model);
else
return PartialView("PartialName", model);
Exactly the same situation. If I'm loading the url in a browser - it returns the full page, master and all.. If I'm loading it later on, using an ajax call - just load the partial view. Simple and easy. And still adheres to the MVC methodology :)
Also, if you're absolutely keen on preselecting the master name.. just do it like this:
return View("ViewName", "MasterName", model);
For my blog I am wanting to use the Output Cache to save a cached version of a perticular post for around 10 minutes, and thats fine...
<%#OutputCache Duration="600" VaryByParam="*" %>
However, if someone posts a comment, I want to clear the cache so that the page is refreshed and the comment can be seen.
How do I do this in ASP.Net C#?
I've found the answer I was looking for:
HttpResponse.RemoveOutputCacheItem("/caching/CacheForever.aspx");
The above are fine if you know what pages you want to clear the cache for. In my instance (ASP.NET MVC) I referenced the same data from all over. Therefore, when I did a [save] I wanted to clear cache site wide. This is what worked for me: http://aspalliance.com/668
This is done in the context of an OnActionExecuting filter. It could just as easily be done by overriding OnActionExecuting in a BaseController or something.
HttpContextBase httpContext = filterContext.HttpContext;
httpContext.Response.AddCacheItemDependency("Pages");
Setup:
protected void Application_Start()
{
HttpRuntime.Cache.Insert("Pages", DateTime.Now);
}
Minor Tweak:
I have a helper which adds "flash messages" (Error messages, success messages - "This item has been successfully saved", etc). In order to avoid the flash message from showing up on every subsequent GET, I had to invalidate after writing the flash message.
Clearing Cache:
HttpRuntime.Cache.Insert("Pages", DateTime.Now);
Hope this helps.
Using Response.AddCacheItemDependency to clear all outputcaches.
public class Page : System.Web.UI.Page
{
protected override void OnLoad(EventArgs e)
{
try
{
string cacheKey = "cacheKey";
object cache = HttpContext.Current.Cache[cacheKey];
if (cache == null)
{
HttpContext.Current.Cache[cacheKey] = DateTime.UtcNow.ToString();
}
Response.AddCacheItemDependency(cacheKey);
}
catch (Exception ex)
{
throw new SystemException(ex.Message);
}
base.OnLoad(e);
}
}
// Clear All OutPutCache Method
public void ClearAllOutPutCache()
{
string cacheKey = "cacheKey";
HttpContext.Cache.Remove(cacheKey);
}
This is also can be used in ASP.NET MVC's OutputCachedPage.
On the master page load event, please write the following:
Response.Cache.SetExpires(DateTime.UtcNow.AddMinutes(-1));
Response.Cache.SetCacheability(HttpCacheability.NoCache);
Response.Cache.SetNoStore();
and in the logout button click:
Session.Abandon();
Session.Clear();
Hmm. You can specify a VaryByCustom attribute on the OutputCache item. The value of this is passed as a parameter to the GetVaryByCustomString method that you can implement in global.asax. The value returned by this method is used as an index into the cached items - if you return the number of comments on the page, for instance, each time a comment is added a new page will be cached.
The caveat to this is that this does not actually clear the cache. If a blog entry gets heavy comment usage, your cache could explode in size with this method.
Alternatively, you could implement the non-changeable bits of the page (the navigation, ads, the actual blog entry) as user controls and implement partial page caching on each of those user controls.
If you change "*" to just the parameters the cache should vary on (PostID?) you can do something like this:
//add dependency
string key = "post.aspx?id=" + PostID.ToString();
Cache[key] = new object();
Response.AddCacheItemDependency(key);
and when someone adds a comment...
Cache.Remove(key);
I guess this would work even with VaryByParam *, since all requests would be tied to the same cache dependency.
why not use the sqlcachedependency on the posts table?
sqlcachedependency msdn
This way your not implementing custom cache clearing code and simply refreshing the cache as the content changes in the db?
HttpRuntime.Close() .. I try all method and this is the only that work for me