Why seperating the View / Controller is important
On my Castle.Igloo post, Jonathan Allen asks in a comment:
I don't understand why you think you have a problem. Consider these passaged from the original definition of the MVC pattern:
"Communication between a view and its associated controller is straightforward because View and Controller are specifically designed to work together. Models, on the other hand, communicate in a more subtle manner."
"Unlike the model, which may be loosely connected to multiple MVC triads, Each view is associated with a unique controller and vice versa. Instance variables in each maintain this tight coupling."
-- Applications Programming in Smalltalk-80(TM): How to use Model-View-Controller (MVC)
http://st-www.cs.uiuc.edu/users/smarch/st-docs/mvc.html
Notice how it explicitly says that there will be a tight coupling between the view and controller.
While your view of MVC is shared by many, I have a hard time reconciling it with the original definition. They are not just variants; they are on literally opposite positions in regards to coupling.
I would like to hear your position on exactly what the MVC pattern is. And if it does in fact differ from the original definition, the reasons why you think the 'new' definition is better.
I think that it boils down to the fact that I have at least some MonoRail experiance, and I tend to think about stuff a little differently about those things now. I mainly focus on seperating the controller from its views, this is done because in WebForms, the other way is not really possible (at least not with a lot more magic, at which point you are with MR but with WebFroms, in which case it is better to use MR in the first place). Seperating the controller from the view helps when you want to unit test it, for instance, or reuse it in a different context.
As for a concrete example for why I would want this speration, here is a simple example:
I have PostsController, which should display a list of posts for the user. This means that I have ViewPosts.aspx page, and because I am using Atlas, there is also a web service over JSON that is used by the page to page between the pages dynamically. Doing this in the traditional way would force me to create two controllers, PagePostsController and WebServicePostsController. But I don't see much point in splitting the responsability. Using Igloo, I can reuse the PostsController in both views, since there is no dependency on a view.
Evne if I assume that I had used an interface to seperate things, I would still get issues, because the Page and the WebService has distinctly different charactaristics. In tests, I could test the controller fully, without needing to mock the view, which makes my job much easier.
The reverse option may be a page that needs to list several things, so it may want to use several controllers (PostsController, CommentsController). I am not sure if this is a good idea yet, but that is possible with Igloo, and I think that it make sense, at least on the surface.
Comments
Why do you not think that Webforms separate the view/controller ?
The aspx is the view, the cs is the controller.
I could go a stretch and say an objectdatasource is the model :)
Because the CS is often doing a LOT of stuff that is directly related to the presentation. At that point it is no longer usable for me.
A simple example:
if(user.IsInRole("admin") || blog.Owner == user || comment.Author == user)
{
btnEditComment.Visible = true;
}
There is a business logic here (who can edit a comment) and presentation logic, showing / hiding the edit comment button.
Take a complex page, now try to dechiper where are the business logic you need to modify, and why you are doing this or that.
Add complex presentation logic, and you are set for a failure.
Good response.
Take your example - how does Monorail do that same task?
This could be a deciding factor for me as a traditional webform developer.
Monorail would have something like:
BlogController.cs
==============
public void ShowPost(int id)
{
// get post, etc
PropertyBag["canEditComment"] = user.IsInRole("admin") || blog.Owner == user || comment.Author == user;
}
showPost.brail - view
=================
<% if canShowPost: %>
${Html.LinkToAction('Foo','Bar', postId)}
<% end %>
The logic about if I should show it and HOW I show it are completely different things.
Where is the hiding of the button code ?
I ask this only in that if you take the .cs code behind and move it to a different class you'd have:
public bool AllowComments()
{
if(user.IsInRole("admin") || blog.Owner == user || comment.Author == user)
{
return true;
}
}
then in the code behind:
bool allowComments = AllowComments();
if(allowComments)
{
btnEditComment.Visible = true;
}
I guess what I'm wondering is if this is more of a question of 'where to I put my business logic'. The aspx/cs model can work as long as you don't clutter it up with business logic - in other words, it handles the decision of whether or not to display a button - it doesn't know much more than that. I think that is more of a good design principle?
Steve,
IMHO, I think the main difference is who got the request. In web form the request are very scattered (all post back activity, plus entire form involved) and it goes thru a 1x steps page cycle with different lifetime for object availability (the famous one is the missing DataItem in GridView, dynamic control creation with missing state data, etc.). In MR, you got entire form data organized into a simple controller method, usually with very few parameters because of the binding is able to create the specific object and map it back to the corresponding method call. So its far easier to deal with than WebForm.
Imagine filling in a contact person, you would end up doing:
public void Button_Click(object sender, EventArgs e)
{
ContactPerson cp=new ContactPerson();
..
.. (lots of field mappings)
cp.Save();
}
(Dont say object data source- you end up with even less flexibility, lots of tricks to deal with the special fields, and lots of aspx xml to write.)
With MR, you are doing:
public void Save([ARDataBind("formContactPerson")] contactPerson)
{
contactPerson.Save();
}
All the field mappings are done. And because the nature of HTTP request, MR is a more natural way to look at it, and you don't lost any flexibility to deal with the HTML/JS directly, such as employing prototype js/Scriptaculous/3rd party JS calendar. And you are not binded to MS only solution.
What I dislike about MVC against classic asp.net, is that the controller pushes it's information to the view, as where I want the view to be able to pull it's information.
Let's say the blog example.
You have a view that lists all blogitems.
The controller pushes this blogitems.
Later on I decide I want the reactions to be displayed on the same page/view. So I first have to modify my controller to get these items (good bye re-usability of the controller, since other views might not use it), and then modify.
If I have views/pages/aspx files that are service aware, I can modify just the view to access the commentsService and call the getMethod of it to have my data to present.
I don't think that I agree with you on that.
If the view starts calling random services, it is starting to get into the realm of too much logic.
I don't have an issue with specialized controllers, though.
In general, I don't like the idea that a controller knows about a view, so what I will usually do is to have the view pull the controller (and only the controller) for the data its needs.
In the scenario that you give, I would usually add a method GetComments() to the controllers, other views do not need it and can safely ignore it.
I've reviewed the Monorail site at Castle. I'm very curious, but I get the sense it's not quite ready to be used, etc..
So, hopefully when it becomes a release out of beta I can take a look at it again.
Is there a reason it's still just 'beta'? Is that because it's not done yet or it has bugs, etc... ?
Is there any other MVC available for asp.net ?
The one area I'm really enjoying with asp.net is the new Atlas and the toolkit.. It saves me hours of time. I'm not much of a javascript programmer, and I find these controls give me much of what I need on the client side without much effort
I do like what I see with Igloo by the way - very nicely done.
I'd like to learn more
MonoRail is beta not because it has bugs, but because we want to reserve the right to make changes to it.
Once we release it we will have to be much more concerned about backward compatibility.
There are numerous places that already use it for production.
There are several other projects that does MVC on ASP.Net, Igloo is the best of them in my opinion, and it still doesn't come close to the ease of use and the clean feel of MonoRail
Perhaps I just don't see the light yet, I mean what's the big difference (imho only a small difference):
if you have a very specific controller for a view, the view knows about it's controller and can ask extra info from it. In turn, the controller asks it's info from BL services.
or you have codebehind class which you can ask info from, which in turn asks from the BL services.
I would see a difference if the controller contained logic of any kind, that wasn't in the service. But all examples I see, just pass a request directly to a BL service and then pass that info to the view. The same way I do with my codebehind files. If it contained logic, you could use the same controller for several view, thus I'd see a PRO for mvc, but not without it.
With MVC:
controller <->view
controller <-> BL service
With ASP.net:
view <-> codebehind class
codebehindclass <-> BL service
//OT: I don't get any confirmation that my comment is posted on your blog
//OT: At least, that was yesterday, seems to work now, sorry 'bout that
About the confirmation of comment posted, I know, I run into this sometimes, I'll look into it.
The difference is that the controller is explicitly not about presentation logic.
The code behind class often has to deal with a lot of presentation logic (if the user is admin, show admin link, if I have permission to edit this post, show edit button on the grid, etc).
This means that there is a high chance of mixing the business and presentation concerns, and that is a Bad Thing.
The difference is that the controller is explicitly not about presentation logic.
The code behind class often has to deal with a lot of presentation logic (if the user is admin, show admin link, if I have permission to edit this post, show edit button on the grid, etc).
This means that there is a high chance of mixing the business and presentation concerns, and that is a Bad Thing.
The difference is that the controller is explicitly not about presentation logic.
The code behind class often has to deal with a lot of presentation logic (if the user is admin, show admin link, if I have permission to edit this post, show edit button on the grid, etc).
This means that there is a high chance of mixing the business and presentation concerns, and that is a Bad Thing.
Yes, I know that's a bad thing, but I still believe the controller isn't that much of a difference.
Take your example of showing an admin link or not.
The controller has to know that the view wants information about if a user is an admin or not. So the controller in doesn't know about the view technically, but the programmer has to build the controller exactly so that the view can use it. This leads to having to know what the view does (and what info that needs) when programming a controller.
To compare to codebehind again:
mvc:The controller sets a property: YES this user is an admin, which the view can then use. and the view uses isVisible='propertyBag["isAsmin"]'
aps.net: The view uses isVisible="IsAdmin()"
the codebehind checks some BL service to see if the user is an admin or not.
Hmmm.. writing this down, I think I see what you mean.
Of course, if the codebehind uses button.visible = IsAdmin (something I don't tend to use, I prefer doing that in the HTML/aspx) THEN you're mixing up logic with display. So, if you use codebehind the 'right' way, it wouldn't be mixing it up. But mvc forces you not to use it the wrong way.... hmm...
MVC can also use:
bool isVisible = controller.IsUserAdmin;
But in general, this is true.
Take into account that there is a large number of issues that cannot be cleanly solved using the ASPX (painting a grid, etc), which cause this issue.
Comment preview