asp.net MVC:hierarchical domain model and controllers - asp.net-mvc

My domain model is this: we have a bunch of schools as the root of the "hierarchy". Each school has teachers and courses, and each course has one teacher. I am trying to model this with the logic of the mvc framework and I 'm quite confused. For example, the \school\details\x should give the first page of a school. That should contain a link to a list of its teachers, and a list to each courses.
A list of teachers means that the index action should be parametric to the school the user is looking at: \teacher\id where id is the school. The same with the course list. And then create teacher or course should also be parametric to what school we are looking at:\teacher\create\x where x=school.
How do I carry around the school id? Is there some neat way to do it, or do I need to pass it around all the time, into every view that needs it? It also makes the site URLs very cryptic. I was thinking of a way to make the url structure like {school-alias}\{controller}\{action}\{id}, still I have to find a way to pass around the school. If this is accomplished, then I need to implement some kind of filter that will not allow a user to perform certain actions if the schoolId he is requesting does not match the one in his profile.
I figure that if I 'm carrying the schoolid around the URL, the site is more REST-like, compared to, for example, getting the schoolId from the user's profile.

I would create acronym for every school. For example:
School no. 1 - ABC
School no. 2 - DEF
If i wanted to list teachers, I would write
http://site-address/ABC/teachers/list or just http://site-address/ABC/teachers
To show basic information about school
http://site-address/ABC
The code for routing would be:
routes.MapRoute(
"Default", // Route name
"{acronym}/{controller}/{action}/{id}", // URL with parameters
new {controller = "School", action = "Details", id = ""} // Parameter defaults
);
I would create authorization action filter on teachers,school and classes controller to check if user has access to school defined by acronym parameter in URL. You can check it by comparing filterContext.RouteData.Values["acronym"] with data stored in profile.

Write an extension method to overload rendering of links that extracts the school identifier ( acronym or whatever you choose to use ) from the routing data and adds it to the route values already passed in. This way your action can choose to use the identifier if it is present but is not required to add it to the view data and you do not have to remember to include it in any action links ( you just have to remember to use your action link overload ).
I would make the action link overload quite obviously different so anyone following behind you can see you are doing something unusual. This could be as simple as Html.SchoolActionLink( ...).
For example:
If your url is http://mydomain.com/abc/teachers/list and your route is defined as {school}/{controller}/{action} then the route value dictionary will have the value "abc" at the key "school". The route values can be accessed via HtmlHelper.ViewContext.RouteData.Values.

In the end I 'm answering my own question.
The real solution to this is :Restfull Routing. It implements the functionality in RoR, which is exactly what I need. Too bad this is not a requirement from more people so that it can go into mvc-trunk.

Related

How to set {city} in all the routes MVC 5

I am developing a marketplace application which is supposed to have different products and vendors which are mapped to different cities.
The idea is that I need to store the CurrentCity in context so that I can use it to construct urls, filter data, fetch delivery areas etc.
e.g.
www.mywebsite.com/cityA/listings
www.mywebsite.com/cityB/listings
www.mywebsite.com/cityA/cart
www.mywebsite.com/cityB/cart
Something like the MoonPig website (https://www.moonpig.com/uk/Gift/Flowers/)
Currently, I am passing the city as a parameter in almost all the controller methods and also storing it in a cookie.
Alternatively, I am also thinking of creating a BaseController and possibly inject it in the OnActionExecuting(ActionExecutingContext context).
But the problem with the first approach is that all the Action methods need to have "city" as a parameter and I need to have it in context for doing something like
#Url.Action("Index", "Listings", new {city = cityName})
If I use the second approach, then I don't think I'll get the urls which have city in them.
I am ideally looking for a solution with which I can inject a city parameter as Base route / segment in the MVC RouteDictionary so that all the Urls are generated accordingly (with #url helper).
Is this possible or is there a better way to tackle this problem?
Would really appreciate if someone can show me a direction.
But the problem with the first approach is that all the Action methods need to have "city" as a parameter
This assumption is incorrect, since MVC automatically passes values from the current context when generating URLs, so there is no need to explicitly pass city as long as it is configured in the route and present in the URL. See this answer for how you can utilize this behavior for localization, which is similar to what you are doing.

Change route during runtime

Currently, we are working on HRIS (Human Resource Information System). We have different user types such as Admin, HR, Employee. But that user types are not static. We want to have different route for each user type.
e.g.
https://website/admin/{controller}/{id}
The route will depend on the user who logged-in in the system. Will read its user type.
May we know if there's a way to configure the route for each user type?
The solution from my point of view would be:
You create a route (URL actually) that matches the pattern:
https://website/admin/3
that holds all the logic for this user - probably, invoking Model, asking for the respective user controller, e.g., superadmin and later on redirecting to...
Another URL matching
https://website/admin/superadmin/3
that now has both controller = superadmin and variable id = 3. It is not said, that the route should be different - you can implement the logic when the controller is either a id (integer) or string to keep the logic more centralized.
Just to mention:
https://website/admin/3
should be fine - you can process the request from the respective Controller without redirecting (see 1. point)
This is very similar to the following question: MVC role-based routing
Essentially you use areas, combined with a routing constraint.

ASP.Net MVC with complex routes - how to keep it "sane"?

I have a client who wishes to use a URL naming convention along the lines of:
/{subjectarea}/{subject}/{action}
Which is fine - this works brilliantly, with one controller per subject area, and having the action after the id (subject) is no issue at all.
However, it then gets complicated, as the client then wants to further continue the hierarchy:
/{subjectarea}/{subject}/{action}/{tightlyrelatedsubject}/{tightlyrelatedsubjectvariables}/{tightlyrelatedsubjectaction}
I have a controller for the tightly related subject (its just another subject area) which handles all of the admin side, but the client insists on having the public view hung off of the parent subject rather than its own root.
How can I do this while avoiding breaking the entire principals of MVC, and also avoiding re-implementing a ton of ASP.Net MVC provided functionality in my subject area controller just to be able to handle the related subjects from that same controller?
Is it possible to somehow call the related subjects controller from within the parent subject controller, and return the resulting view (as this would keep the separation of functionality for the subjects to their own controllers)? If that is possible, it would solve a heck of a lot of issues with this.
Here is the solution which solves my given issue - hope it solves someone elses.
As mentioned in my comment to Robert Harvey, all I actually need is another route which doesn't use the first two or three components as the controller, action and id, but instead takes those values from later on - if you hang this off of a static value in the route as well, its much easier to do.
So, here is the url I decided on to simplify the route:
/{subjectarea}/{subject}/related/{tightlyrelatedsubject}/{tightlyrelatedsubjectvariables}/{tightlyrelatedsubjectaction}
The route which satisfies this URL is as follows:
routes.MapRoute(
"RelatedSubjects",
"{parentcontroller}/{parentsubject}/related/{controller}/{id}/{action}",
new { controller = "shoes", action = "view", id = "all" }
);
On the subsequent controller action, I can ask for parameter values for parentcontroller and parentsubject so I can filter out the related item to just be specific to the given parent subject - problem solved!
This route needs to be above the ones which just deal with the first two values, otherwise you run the risk of another route map hijacking the request.
I could do this entirely without the /related/ static portion as the route could easily match on number of values alone, and infact I may indeed do so - however, I consider it better for later administration if there is a static item in there to confirm the use of the route.
I hope this helps someone!
One way you can do it is specify a wildcard route (notice the asterisk):
routes.MapRoute("subjects", "{action}/{*path}",
new { controller = "Subjects", action = "Index" });
This allows the controller to receive the entire path string after the action.
You can then obtain the hierarchy of subjects in the controller method like so:
string[] subjects = path.Split('/');
Once you have that, you can do anything you want, including dispatching different subjects to different handling methods for processing.

asp.net mvc dynamic/relative routing

I'm developing a website which has a modular structure.
Every segment of the url presents an content item.
For example url: www.mysite.com/blogs/programming/2010/01/
Root item is 'blogs' of type 'area'. It has a child item 'programming' of type 'blog'.
Now there's '2010/01' left of the url.
Last valid (routable) item 'programming' was a blog so I need to map '2010/01' to action
BlogController.Date(int blogid, int year, int? month, int? day)
Every controller comes from a module (separate dll), which registers some item types (blog registers types 'blog' (routable) and 'post' (not routable). 'blog' can have children of type 'post').
When last valid (routable) item of the url is detected, logic knows which assembly and controller to look for. Now I need a way to invoke correct action with correct parameters.
Some routes for item of type 'blog'
{year}/
{year}/{month}
{year}/{month}/{day}
feed/
category/{category-name}/
tag/{tag-name}/
search/{*phrase}
{*post-name}
Any suggestions what would be a simple way to do the routing?
To solve the action parameter signature problem, I personally would create a new Model class "BlogModel" and have only that as your single parameter. This way, you'd have a consistent action parameter signature. However, this would require a bit more work, as you would need to create a custom ModelBinder object "BlogModelBinder" and register it to the ModelBinderFactory (or in MVC3 the DependencyResolver). In the "BlogModelBinder" you simply look up the RouteData's parameters and values and bind it to the corresponding field in your "BlogModel."
From my personal experience, I don't think there's an easy way to register your routes: you still would have to individually register the route urls to a specific action. Unless someone has an efficient way of registering the route urls, you can take solace in knowing that we all have to get our hands dirty with the plumbing code.

What is the appropriate route design for this ASP.NET MVC application?

My url http://server/region/section/item
Now if someone goes to http://server/us/ I want to display a page to choose a section
if someone goes to http://server/us/beer/ I want to display a list of all beers.
My question is should I just have 1 route with defaults and then return different views depending on how much the URL is filled in or should I create multiple routes going to the same controller different actions? or even different controllers, I just want to know what the best practice is.
The typical route looks like this:
http://www.domain.com/controller/action/id
[domain/controller/action/id]
In your case, it's short one part:
http://server/us/beer
[domain/controller/action?/?]
As Robert Harvey said, you wouldn't want to have an action for every product. So how about something like:
http://server/us/product/beer
[domain/controller/action/id]
domain = server
controller = us (tho, this doesn't seem like it would be a good name for the controller)
action = product
id = beer
Then you'd develop a product view that would show the beer data to your visitors.
This isn't architect-ed very well, but without knowing your situation it would be difficult for anyone to answer this. I hope this helps though.
In your particular case, "beer" can probably be a filter to a controller action, rather than another controller action, in which case you need only one route entry.
It doesn't seem wise to create a strategy that will require you to add new routes, controller methods and/or views every time you add a product category. Unless, of course, you want to highly customize each category's look and behavior.

Resources