Recently I was trying to add a simple message to the top of the screen if the user had been logged out.
This message, should appear only once. If the user navigates away from the page, it shouldn’t show again.
So, it made sense to use TempData
In my controller, I had something like this:
1 2 3 4 |
_authenticationManager.SignOut(DefaultAuthenticationTypes.ApplicationCookie); TempData["LoggedOut"] = "True"; return RedirectToAction("Index", "Home", new { area = "" }); |
Then, in my view, a simple check:
1 2 3 4 5 6 |
@if (TempData.ContainsKey("LoggedOut")) { <div class="alert alert-info" role="alert"> You have been logged out. </div> } |
The issue is here, using ContainsKey doesn’t mark the item for deletion. So on page reload, the TempData dictionary still contains that key, so the message is still displayed.
The solution is to change the ‘if’ statement to
1 |
if (TempData["LoggedOut"] != null) |
Reading (by accessing the dictionary item by index) marks the item for deletion on the next request.
I’ve recently been looking in to building a directory type application with Asp.Net MVC
One of the requirements would be to have SEO friendly URLs such as:
http://www.mysite.com/restaurants/Camberley—Surrey
/restaurants being the “Category”
/Camberley—Surrey being the “Location”
So, I created a ‘CategoryController’ like this
1 2 3 4 5 6 7 8 |
public class CategoryController : Controller { public ActionResult Index(string category, string location) { //do stuff return View(); } } |
But how do I map my routes?
What you can use, is RouteConstraints
Simply map your route as usual, but add an instance of our custom RouteConstraint
1 2 3 4 5 6 7 8 9 10 11 12 |
routes.MapRoute( name: "CategoryRoute", url: "{category}/{location}", defaults: new { controller = "Category", action = "Index", location = UrlParameter.Optional }, constraints: new { category = new CategoryRouteConstraint() } ); routes.MapRoute( name: "Default", url: "{controller}/{action}/{id}", defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional } ); |
With our CategoryRouteConstraint as follows:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
public class CategoryRouteConstraint : IRouteConstraint { public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection) { //these would usually come from a database, or cache. var categories = new[] { "restaurants", "cafes", "bistros" }; if (values[parameterName] == null) return false; //get the category passed in to the route var category = values[parameterName].ToString(); //now we check our categories, and see if it exists return categories.Any(x => x == category.ToLower()); // url such as /restaurants/Camberley--Surrey will match // url such as /pubs/Camberley--Surrey will not } } |
Of course, our collection of ‘categories’ would come from some kind of store – a database, cache, etc…
Now, if we run the app and visit /restaurants/Camberley–Surrey, our route resolves as we expect.
Demo: On github
I’ve been scratching my head about this one for sometime…
As I’m sure your aware (or you wouldn’t be reading this) in the Global.asax file, we have the following method:
1 2 3 |
protected void Session_Start(object sender, EventArgs e) { } |
Placing a breakpoint on this method, I attached the debugger, ran up my application, and the breakpoint was not hit…
What I would of expected to happen would be the debugger break just as the page is loaded. It didn’t.
When my application builds, I have post-build events that copy required files (.aspx, bin dir etc…) to a separate directory, and i use a custom web server for the project (rather than the standard, local IIS Web server)
What this means is that the Global.asax file needs to be in my output directory.
Nothing in the “Global” class will run without the presence of the Global.asax file.
My post-build events did not copy *.asax files.
Upon adding this line, the issue was fixed, and my breakpoint is now hit.