1

I'm developing an MVC3 application and I have a page (well, a view) that let the users edit document's metainfo (a classic @Html.BeginForm usage). For general documents users will see standard fields to fill up, but through a dropdownlist they will be able to specify the type of the document: this, through an ajax call, will load new fields on the edit-document-form. Whem the user submit the completed form, at last, the controller should read all the standard fields, plus all the fields loaded as being specific to the type of document selected.

Question is, how can I handle all this extra fields in a controller? Say that I have Document class and a bunch of other classes extendinf Document, like Contract : Document, Invoice : Document, Complaint : Document and so forth, each having specific property (and this fields loaded on the form), how do I write the action in the controller?

I thought to use something like (I'll omitt all the conversions, validations, etc, for brevity)

[HttpPost]
public ActionResult Save(dynamic doc)
{
    int docType = doc.type;
    switch (docType)
    {
        case 1:
            var invoice = new Invoice(doc);
            invoice.amount = Request.Form["amount_field"];
            invoice.code = Request.Form["code_field"];
            //and so forth for every specific property of Invoice
            Repository.Save(invoice);
            break;
        case 2:
            var contract = new Contract(doc);
            contract.fromDate = Request.Form["fromDate_field"];
            contract.toDate = Request.Form["toDate_field"];
            //and so forth for every specific property of Contract
            Repository.Save(contract);
            break;
        ..... // and so forth for any document types

        default:
            break;
    }
}

But it seems a very dirty approach to me. Do you have a better idea on how to achive this? Maybe there's a pattern that I don't know nothing about to approach this kind of scenario.

Update

A second idea comes to my mind. After commenting Rob Kent's answer, I thought I could take a different approach, having just one class Document with a property like

public IEnumerable<Field> Tipologie { get; set; } 

where

public class Field
{
    public int IdField { get; set; }
    public String Label { get; set; }
    public String Value { get; set; }
    public FieldType ValueType { get; set; }
    public List<String> PossibleValues { get; set; } // needed for ENUMERATION type
}

public enum FieldType 
{
    STRING, INT, DECIMAL, DATE, ENUMERATION
}

Is this a better approach? In this case I can have just an action method like

[HttpPost]
public ActionResult Save(Document doc)

But shoud I create the fields in the view in order to make the MVC engine do the binding back to the model? Given that the class inheriting from Document in the first approach will probably be generated at run-time, would you prefer this second approach?

themarcuz
  • 2,573
  • 6
  • 36
  • 53
  • Yes, this would work because you are just creating a property bag. Does it actually help you with distinguishing the type of Document though? You will end up with a loosely typed document but since you said in your reply that you don't know what types you have, that is a constraint you will have to live with. – Rob Kent May 09 '12 at 09:57

1 Answers1

0

To keep it all hard-typed on the server, you could use an abstract base type with a custom binder. See my answer here to see how this works: MVC generic ViewModel

The idea is that every time they load a new set of fields, you change the BindingType form variable to the instantiated type of the handler. The custom binder is responsible for creating the correct type on submission and you can then evaluate that in your action, eg:

if (model is Contract) ...

I'm not sure if you will be able to set up different actions each with a different signature, eg,:

public ActionResult Save(Contract contract) ...
public ActionResult Save(Invoice invoice) ...

Pretty sure that won't work because Mvc will have already decided which method to call, or maybe it will firstly see what type it gets back and then decides.

In my linked example, I am checking for overridden base members but if that is not an issue for you, you just need to create the correct type.

Community
  • 1
  • 1
Rob Kent
  • 5,183
  • 4
  • 33
  • 54
  • As far as I know (and I tried it now) mvc doesn't support for different action with same name but different signature on the same controller, unless you specify different Http method. Moreover, in the real application, I won't know which and how many classes deriving from `Document` there will be: all these classes will be dynamically created and compiled at run-time, based on values on the DB. – themarcuz May 09 '12 at 08:30