Currently I have a folder structure like this:
Area (folder)
- Toolkit (folder)
- Controllers (folder)
- AdminController.cs
- Views (folder)
- Admin (folder)
- Privledges (folder)
- Create.cshtml
- Edit.cshtml
- Delete.cshtml
Which translates to
/Toolkit/{controller}/{action}/{tool}/{id}
Is it a bad practice to set up the action to behave a like a controller that serves up a view based on the string {tool} parameter and parameter {id} passed to the action?
The implementation of what I am talking about:
private const string FOLDER_PRIVILEGES = "./Privileges/";
public ActionResult Privileges(string tool, string id = "")
{
dynamic viewModel = null;
ToolViews view; // enum for the views
// Parse the tool name to get the enum representation of the view requested
bool isParsed = Enum.TryParse(tool, out view);
if (!isParsed)
{
return HttpNotFound();
}
switch (view)
{
case ToolViews.Index:
viewModel = GetIndexViewModel(); // call a function that gets the VM
break;
case ToolViews.Edit:
viewModel = GetEditViewModelById(int.Parse(id)); // sloppy parse
break;
default:
viewModel = GetIndexViewModel();
break;
}
// The folder path is needed to reach the correct view, is this bad?
// Should I just create a more specific controller even though it would
// require making about 15-20 controllers?
return View(FOLDER_PRIVILEGES + tool, viewModel);
}
When I write a View, I have to make sure the Path name is used for the folder
@Html.ActionLink("Edit", "./Toolkit/Admin/Priveleges/Edit", "Admin", new { id = item.id })
This seems to be a poor practice, because if the folder structure changes at all it will require a lot of maintenance.
However, if I have to break out the actions into controllers there would be many of them (almost 20 with more added over time).
If what I am doing is a bad practice, what would be the best way to serve a route that looks like this?
/Toolkit/Admin/Privileges/Edit/1
I want to avoid doing the following:
/Toolkit/Admin/CreatePrivileges/1
/Toolkit/Admin/EditPrivileges/1
/Toolkit/Admin/DeletePrivileges/1
Please let me know if I'm not making any sense, because I am having a hard time putting this question into words.
I think you're trying to force a convention into MVC that goes against its orignal intent.
With MVC, your controller is a noun, and your action is a verb. Using your examples, you have:
So as you can see, if you can consider Toolkit + Admin as area + subarea, or combine them into one area (TookitAdmin), it would get you back to the original purpose for controllers and actions.
Based on the comments, it sounds like you may have decided to go this way. But I wanted to point out that the conclusion you came to in a round-about way is getting back to the roots of MVC.
As a side note, have you considered moving to MVC4? Its Web API provides better support for a RESTful API, which it sounds like you may be trying to get to.
Not an answer to the original question, but the OP asked for a sample of an Enum constraint rather than having to check the enum in each action. ie:
// Parse the tool name to get the enum representation of the view requested
bool isParsed = Enum.TryParse(tool, out view);
if (!isParsed)
{
return HttpNotFound();
}
Instead of having to accept the enum value (Tool, in this case) as a string, you can force the value to come into your action already cast as the appropriate enum. An added benefit to this is that the MVC framework will take care of returning the correct response (HttpNotFound
) in this case.
This is your constrain method. It accepts any type of Enum. There is no need to create a separate constraint for each Enum.
public class EnumConstraint<T> : IRouteConstraint where T : struct
{
private readonly HashSet<string> enumNames;
public EnumConstraint()
{
string[] names = Enum.GetNames(typeof(T));
this.enumNames = new HashSet<string>(from name in names select name.ToLowerInvariant());
}
public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection)
{
return this.enumNames.Contains(values[parameterName].ToString().ToLowerInvariant());
}
}
Then, in your RegisterRoutes
method (MVC4) or global.asax.cs page (MVC3), you just register your route like this:
routes.MapRoute(
url: "/Toolkit/Admin/{Action}/{id}",
constraints: new { Action = new EnumConstraint<ToolViews>(), id = @"\d+" }
);
I also added a number constraint on the id
parameter to save you having to parse that as well.
Let me know how that works out for you.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With