What role does the User Control has in the ASP.Net MVC?

Several developers have asked me about the use of User Controls within the ASP.Net MVC, so I decided to write down what I think about it.

A User Control is a piece of the User Interface which is sometimes created to be reused on several of Web Forms, some are created as a result from a Refactoring of the User Interface code. A User Control has a code-behind and the User Control is living its own life mostly if the Web Form and User Control is well defined. Some people tend to access the Web Form from a User Control and I think that is bad practice, it will make the User Control dependent to a specific Web Form, so a better solution is to use events which a Web Form can subscribe to, if we need to pass data from the Web Form to the User Control, we can do it by adding properties to the User Control. Now how about a User Control in the ASP.Net MVC?

As you all may know there is a View and the View has a Controller, the Controller work like a contract between the View and the Model. The Model is where we have our Business Logic and domain entities.

NOTE! I have seen several developers using the Controllers as a replacement of Business object, that is not the role of a Controller, the Business logic should be in the Model.

I will assume that a View has only one Controller and should only be dependent on one, so in the ASP.Net MVC the User Controls will take a "new" kind of role. A User Control in the ASP.Net MVC is part of the View and should reuse the same Controller as the View. If not, our View will have dependency to several different Controllers and we will need to take this into consideration when writing our test. We should try to avoid as many dependency as possible, too many dependency will make the code harder to read and maintain etc. So what role does the User Control take in a View? They will be a result of Refactoring of the User Interface code, and will more or less be like a simple "include" file used by the VIew (Like a extracted method in a Class). A User Control and a View together will create a complete View and this complete View should only work against one Controller. Some developers tend to create and use a User Control after the same pattern they are used to when they work with Web Forms. Instead of using a code-behind, they bind the User Control to it's own Controller. By doing so, a View which will use the User Control, will have a indirect dependency to several Controllers. Instead of making sure a View has its own Controller, it now has several, if we have several User Controls on the same View and all User Controls uses their own Controllers, we will sooner or later create a Death Star. If we should use a User Control in the ASP.Net MVC it's the Controller of the View which will return the Model which the User Control and View will use, right?! So it's the same Controller that should handle the User Controls actions. If we want to create a User Control and see it as a self lived unit, I think it should be important to let its own Controller give the Model to the User Control, but how it's designed today, we can't. It's the Controller used by the View which must also get the Model for the User Control.

How about reusing a User Control on different Views if they should more or less serve as a "include" file and use the same Controller as the View? For me it's all fine, because the User Control will share the same Controller as the View, and the User Control should try to avoid having its own form element. If we want to reuse a User Control on several of Views, where the User Control has its own Controller, the Controller of the View will have a indirect dependency to the User Control and suddenly the Controller of a View will have several reason to change (If the User Control need to be updated and that may lead to an update of the Controller.). To solve this, we shouldn't try to reuse a User Control as long as it can't server as a simple include of HTML element, where the User Control will not have a form element pointed to a separate Controller served only by the User Control.

I hope some of you can try to convince me if my assumptions is totally wrong, or at least you can try ;)

9 Comments

  • I've found a very useful way to re-use User Controls across different views. Say I have a business object, say Person. I create a User Control called EditPerson.ascx, and bind its ViewData.Model to a Person object.

    Now I can create a form in this user control which will edit a person (name, address, telephone number, etc).

    I then have two actions in my PeopleController; New() and Edit(int id). The first action generates a new (blank) Person record and sends it to the View; the second loads a specified Person and sends it to the Edit view.

    Each of these views contains an EditPerson.ascx file - this simply builds the "innards" of the HTML form (I tend to keep the actual form tags outside the user control). Now, my edit user form is consistent between creating and editing!

    Hope this makes sense!


  • @Keith Williams:
    In your case you make sure the View handles the <form> tags, in this case you reuse the User Controls as it was an "include" file and let the Controllers handle the action. So you don’t bind a specific User Control to its own Controller, instead you reuse the same Controller the View uses, right? If that is the case, it’s how I think the User Controls should be used, not as a self lived View inside of another View.
    @Robert:
    Good point..
    We do have an exception and that is portal solutions, where each Web Part has it’s own functionality, so should the View’s Controller give the Model to those Web Parts, or should each Web Part get it’s own data and how will that work if the MVC don’t support partial loading or saving out of the box Hmm, interesting.. but I shouldn't use ActionFilters to solve this. ActionFilter for me is more about pre and post conditions to separate crosscutting concerns. I see the data displayed on the View is part of the core concern, and it's the Controller's responsibility to return the correct Model to the View, so instead of Filters I should have used a base class and a good designed presentation model. A portal often add Web Parts dynamically, and we can't dynamically add code to the Controllers if we don't have AOP support. So in this case the Controller must know which Web Part the View uses and what Model to return. If we have static Web Parts, it's easy to solve.. I prefer a well defined presentation model and letting the Controller get the View, rather than using ActionFilter. NOTE: I don't say that ActionFilter is a bad solution to solve the problem, but I shouldn't use it..


  • @Torkel:
    "I see UserControl in MVC as partial (reusable) parts of views. The main point of partial views is to take a common piece of ui markup and make it reusable in many views (and also to make the main view more readable and clean)."
    Yes, that is true. But if you have one or more User Controls added to the page and they used their own Controllers, you can't write a test to test the View, you have to write several test to also include the User Controls controllers. What I want to do is to have one test for the View, not several different because of a View will indirect be dependent on several Controllers. So instead I should as a best practise, not make self lived User Controls, instead handle them as "include" file which share the same Controller as the View, in another way, add no <form> tag to the User Control. There as exceptions, but I shouldn't start to design User Controls as self lived units. If we create a self lived User Control, we should not see them as a part of the View, in that case it's fine to bind them to a specific controller, because we want to test them isolated.

  • What??? Partial views that have their own controller? I haven't seen those, maybe MonoRails ViewComponent can be seen as a sort of subcontroller, but UserControls as a partial view has no controller in the same way as a view has no direct dependency on a specific controller. Views are dependent on view data, true they might refer to controller actions via links and post actions. I see partial views as a slice of view markup that can be reused. For example a partial view "menu" top level links, a "login" partial view that handles login/logout for all pages (this one is probably placed in a master layout), partial "person link" view that renders a person link with an image that can be used in multiple views, etc, etc.

    You speak of testing views, in what way do you test views? and what do you mean that UserControls have their own Controller?


  • @Torkel:
    A Browser is a Browser, so instead of open up a new Window for a form, some people want to reuse the space they have in one page and separate the pages into different isolated zones. This is done by using a User Control which has a <form> inside the User Control and bind it to a specific Controller, nothing wrong so far, but the problem is that the View’s Controller is the one that gets the Model even for the User Controls, there is no way to make sure a User Control can load its own Model, as we can in a Web Form solution by using the Page_Load event of the User Control. So we don’t have a "partial" load feature, maybe the word "separate" loading would be better than using the world "partial". If you want to handle common actions on views like Login/Logout, it should be part of the Controller the View uses; those actions can be shared among Controllers by using a base class for the Controllers. You should try to not make a View to be dependent of several Controllers, which will be the case if a User Control uses its own Controllers and t can lead to needles complexity. If a User Control could live in an isolated mode, where the User Control can interact with a Controller for retrieving the model is should display, then we have a good separation of concerns (The View’s Controllers don’t need to get the Model for the User Control). So how is it done today, the View’s Controller has the responsibility to get the Model for the User Control, and it should be the Views controller that takes care of the User Controls actions not a separate. It’s like having a CustomerRepostiory to retrieve a Customer, and an XXXXRepository to save the Customer. It’s the same Repository that gives us the Customer, which should update and save the Customer, not separated into two different classes.
    About testing the View. It’s basically the Controller used by the View I will test, but what I often use is a presentation model which I also test, in that case the test against the Controller and its presentation mode. I have one Controller to test against to see that all my View’s "actions" will work, instead of using several Controllers, which I may need if a User Control will work against its own Controller.

  • When you want to use a MVC user control on several views, you can bind the user control to a Interface, and implement this interface on all models in which you want to use your user control.
    This works very well.

    Making a user control to be used only ONE place, is wrong.

  • Usercontrols inside a view is a weak story. MVC has 3 letters because it is a triad. Look at Taligent they supported the idea of a controller using subcontrollers in a view. Excluding the usercontrol from having a corresponding controller limits the granularity of the composition architecture of MVC.

  • "...the User Control should try to avoid having its own form element."

    This restriction is not going to flow long term for the idea.

  • in MVC, I am using user control
    when i view page first time means load time, application run properlly & view the page on screen.

    but....

    when i add some data in textboxs or any any other contols & Save that record in the database, it save the data but in view page it display an error on user control.

    Error is:
    "Validation of viewstate MAC failed. If this application is hosted by a Web Farm or cluster, ensure that configuration specifies the same validationKey and validation algorithm. AutoGenerate cannot be used in a cluster."

    Pls help me.

Comments have been disabled for this content.