M.R.
M.R.

Reputation: 4837

Dynamic form with no real OOP or objects?

I am tacking a large refactor of a project, and I had asked this question to confirm/understand the direction I should go in and I think I got the answer that I wanted, which is not to throw away years worth of code. So, now begins the challenge of refactoring the code. I've been reading Martine Fowler and Martin Feathers' books, and they have a lot of insight, but I am looking for advice on the ultimate goal of where I want the application to be.

So to reiterate the application a little bit, its a dynamic forms system, with lots of validation logic and data logic between the fields. The main record that gets inserted is the set of form fields that is on the page. Another part of it is 'Actions' that you can do for a person. These 'Actions' can differ client by client, and there are hundreds of 'Actions'. There is also talk that we can somehow make an engine that can eventually take on other similar areas, where a 'person' can be something else (such as student, or employee). So I want to build something very de-coupled. We have one codebase, but different DBs for different clients. The set of form fields on the page are dynamic, but the DB is not - it is translated into the specific DB table via stored procs. So, the generic set of fields are sent to the stored proc and the stored proc then decides what to do with the fields (figure out which table it needs to go to). These tables in fact are pretty static, meaning that they are not really dynamic, and there is a certain structure to it.

What I'm struggling specifically is how to setup a good way to do the dynamic form control page. It seems majority of the logic will be in code on the UI/aspx.cs page, because its loading controls onto the webpage. Is there some way I can do this, so it is done in a streamlined fashion, so the aspx.cs page isn't 5000 lines long? I have a 'FORM' object, and one of the properties is its' 'FIELDS'. So this object is loaded up in the business layer and the Data layer, but now on the fron end, it has to loop through the FIELDS and output the controls onto the page. Also, someway to be able to control the placement would be useful, too - not sure how do get that into this model....

Also, from another point of view - how can I 'really' get this into an object-oriented-structure? Because technically, they can create forms of anything. And those form fields can represent any object. So, for example, today they can create a set of form fields, that represent a 'person' - tomorrow they can create a set of form fields that represent a 'furniture'. How can I possibly translate this to to a person or a furniture object (or should I even be trying to?). And I don't really have controls over the form fields, because they can create whatever....

Any thought process would be really helpful - thanks!

Upvotes: 4

Views: 672

Answers (3)

Simon Halsey
Simon Halsey

Reputation: 5480

I found xml invaluable for this same situation. You can build an object graph in your code to represent the form easily enough. This object graph can again be loaded/saved from a db easily.

You can turn your object graph into xml & use xslt to generate the html for display. You now also have the benefit of customising this transform for differnetn clients/versions/etc. I also store the xml in the database for performance & to give me a publish function.

You need some specific code to deal with the incoming data, as you're going to be accessing the raw request post. You need to validate the incoming data against what you think you was shown. That stops people spoofing/meddling with your forms.

I hope that all makes sense.

Upvotes: 0

Trevor
Trevor

Reputation: 13457

How can I possibly translate this to to a person or a furniture object (or should I even be trying to?)

If I understand you correctly, you probably shouldn't try to convert these fields to specific objects since the nature of your application is so dynamic. If the stored procedures are capable of figuring out which combination of fields belongs to which tables, then great.

If you can change the DB schema, I would suggest coming up with something much more dynamic. Rather than have a single table for each type of dynamic object, I would create the following schema:

Object {
  ID
  Name
  ... (clientID, etc.) ...
}

Property {
  ID
  ObjectID
  Name
  DBType (int, string, object-id, etc.)
  FormType ( textbox, checkbox, etc.)
  [FormValidationRegex] <== optional, could be used by field controls
  Value
}

If you can't change the database schema, you can still apply the following to the old system using the stored procedures and fixed tables:

Then when you read in a specific object from the database, you can loop through each of the properties and get the form type and simple add the appropriate generic form type to the page:

foreach(Property p in Object.Properties)
{
  switch(p.FormType)
  {
    case FormType.CheckBox:
      PageForm.AddField(new CheckboxFormField(p.Name, p.Value));
      break;
    case FormType.Email:
      PageForm.AddField(new EmailFormField(p.Name, p.Value));
      break;
    case FormType.etc:
      ...
      break;
  }
}

Of course, I threw in a PageForm object, as well as CheckboxFormField and EmailFormField objects. The PageForm object could simply be a placeholder, and the CheckboxFormField and EmailFormField could be UserControls or ServerControls.

I would not recommend trying to control placement. Just list off each field one by one vertically. This is becoming more and more popular anyway, even with static forms who's layout can be controlled completely. Most signup forms, for example, follow this convention.

I hope that helps. If I understood your question wrong, or if you'd like further explanations, let me know.

Upvotes: 2

havardhu
havardhu

Reputation: 3616

Not sure I understand the question. But there's two toolboxes suitable for writing generic code. It's generics, and it's reflection - typically in combination.

I don't think I really understand what you're trying to do, but a method using relfection to identify all the properties of an object might look like this:

using System.Reflection;
(...)
public void VisitProperties(object subject)
{
    Type subjectType = subject.GetType();
    foreach (PropertyInfo info in subjectType.GetProperties()
    {
         object value = info.GetValue(subject, null);
         Console.WriteLine("The name of the property is " + info.Name);
         Console.WriteLine("The value is " + value.ToString());
    }
}

You can also check out an entry on my blog where I discuss using attributes on objects in conjunction with reflection. It's actually discussing how this can be utilized to write generic UI. Not exactly what you want, but at least the same principles could be used.

http://codepatrol.wordpress.com/2011/08/19/129/

This means that you could create your own custom attributes, or use those that already exists within the .NET framework already, to describe your types. Attributes to specify rules for validation, field label, even field placement could be used.

public class Person
{
    [FieldLabel("First name")]
    [ValidationRules(Rules.NotEmpty | Rules.OnlyCharacters)] 
    [FormColumn(1)]
    [FormRow(1)]
    public string FirstName{get;set;}

    [FieldLabel("Last name")]
    [ValidationRules(Rules.NotEmpty | Rules.OnlyCharacters)]
    [FormColumn(2)]
    [FormRow(1)]
    public string LastName{get;set;}
}

Then you'd use the method described in my blog to identify these attributes and take the apropriate action - e.g. placing them in the proper row, giving the correct label, and so forth. I won't propose how to solve these things, but at least reflection is a great and simple tool to get descriptive information about an unknown type.

Upvotes: 0

Related Questions