views:

193

answers:

3

First post so please be gentle :)

When creating user controls in ASP.NET MVC, what is the best way to structure the code so that the controllers that invoke views that use the user controls do not all have to know so much about the controls? I would like to know a good way to maintain DRY while using user controls in ASP.NET MVC.

Please note, this question only pertain to user controls that require special handling and logic on a postback. I have no problem creating nice DRY code for user controls that are either view only (using RenderPartial) or that require some pre-processing to create the appropriate ViewModel (using RenderAction).

Also, this question pertains only to achieving reusable controls within an application. I am not worried about reusability between applications at this point.

To give a specific example, let's say I would like to create a 'Quick Add' user control which contains three entry fields, First Name, Last Name and Company Name and a submit button. When the QuickAdd functionality is used, the following steps should be performed independent of what page the control is on:

  1. Validate that the fields were not empty, if they are, show an indicator.
  2. Perform a lookup to a repository to see if the Company already exists, if not; create it.
  3. Create a new contact associated to either the existing company or the newly created company
  4. Re-render the existing page. If no validation errors, the user would see the exact same page again, otherwise the same page with validation errors.

My main problem with achieving DRY has to do with all the controllers that invoke views that contain the partial view end up having to have an Action Method to process the form submission from the Quick Add. Even if I break out the logic for processing the information into a separate controller and invoke that method from each of the other controllers it seems like a burden that each and every controller that invoke views that have reusable controls have to have that knowledge.

The other option I looked at was to have the reusable control always submit to a specific action method / controller but then there is no way for that controller to know how to re-populate the model appropriately for the specific controller that invoked the view that contained the reusable control (in step 4).

I am aware that there is talk of subcontrollers in MVC 2 (from this question http://stackoverflow.com/questions/1244911/asp-net-mvc-contained-user-controls) but since it is not there yet, what is the best way to structure the code to achieve maximum reusability while maintaining DRY?

Is there an alternative to having to have all the controllers that invoke views that use a reusable control (with the characteristics of the one described above), having to have an Action Method to process the information from the control?

A: 

Hey,

I'm not sure why you say the Quick Add form has to have an action method in each controller that uses it; if you wrap the Quick add functionality in a Html.BeginForm(); Html.EndForm() combo, you can have the beginform method specify the name of the action and controller, so you only need one controller.

I understand where you are coming from; it's something I have been thinking about to. While I don't know all the answers, I have some ideas for you to consider. Every controller action method is invoked via a ControllerActionInvoker class, which you can customize. This class handles invoking all of the action methods, so here you could embed certain aspects of reusable code across all or certain action methods.

Look into filters too, as there are a variety of filters that you can use or customize that fire for action methods that implement it. This way, code can run before and after the action method execution and result execution.

For validation, there is already validation components built in that will prevent page submission... you could also consider XVAL which has some other nice features. The Unity framework is an IOC container framework, which dynamic injection keeps things loosely coupled and DRY, as you can inject all kinds of references.

Also, you mentioned subcontrollers; the MVC preview has additional features you may be interested in... for instance, it has a RenderAction method that can render an action method within another action's view.

Hopefully that helps... so what am I missing?

Brian
Thanks for the reply, I will take a look at the ControllerActionInvoker.For the BeginForm and EndForm, it is one of the options I looked at but the problem has to do with how to create the model for the specific page the reusable control happens to be used from (the requirement is to re-render the same page in step 4).
I often use a hierarchy of models; make part of that logic a partial view, and associate with it a view model just for that partial. Then, each view that uses the partial view has a model that has a property with an instance of that partial view model, so:public class XModel public YPartialViewModel Y { get; set; }
Brian
A: 

Have a look at RenderAction and RenderPartial. Those are the canonical ways to arbitrarily inject a common control into a view.

Use RenderPartial when you want to include the data as part of your ViewData infrastructure.

Use RenderAction when you want the data to be separate from the ViewData infrastructure. The data will come from the controller method you specify in RenderAction.

Check out the NerdDinner tutorials, if you haven't done so already.

Robert Harvey
Yeah, RenderAction is good for being able to achieve isolated control and create a ViewModel specifically for the User Control prior to its rendering but neither RenderPartial or RenderAction is useful (or rather not applicable) when isolated control is needed to process the contents of a user control on a postback.
+2  A: 

At the end of your post, you ask "Is there an alternative to having to have all the controllers... having to have an Action Method to process the information from the control"

The answer for that question is to write a custom model binder. Your custom model binder can be responsible for the populating the values from the incoming form control(s) into model or properties used by all of the controllers. Normally, you want to separate the validation from the model binding, but there is no reason that you couldn't combine them as well.

I highly recommend 6 Tips for ASP.NET MVC Model Binding for a deeper discussion of the topic along with some good references.

Chris Melinn
Thanks, custom model binding will definitely be very useful in these situations since it allows to factor out the logic for reconstructing ViewModel objects used by partial views (and otherwise) on postbacks, so that controllers do not need to be burdened by that knowledge.
Thanks for your comment, I'm glad it helped. And welcome to StackOverflow as well :-)
Chris Melinn