开发者

Dynamic form creation in asp.net c#

So, I need some input refactoring an asp.net (c#) application that is basically a framework for creating dynamic forms (any forms). From a high level point of view, there is a table that has the forms, and then there is a table that has all the form fields, where it is one to many between the two. There is a validation table, where each field can have multiple types of validation, and it is a one to many from the form fields table to the validation table.

So the issue is that this application has been sold as the be-all-end-all customizable solution to all the clients. So, the idea is whatever form they want, we can build it jsut using DB configurations. The thing is, that is not always possible, because there is complex relationship between the fields, and complex relationship between the forms themselves. Also, there is only once codebase, and this is for multiple clients - all of whom host it on their own. There is very specific logic for each of the clients, and they are ALL in the same codebase, with no real separation. Sometimes it was too difficult to make it generic, so there are instances where it has hard coded logic (as in if formID = XXX then do _). You can also have nested forms, as in, one set of fields on its own within each form.

So usually, when one client requests a change, we make the change and deploy it to that client - but then another client requests a different change, and we make the change and deploy it for THAT client, but the change from the earlier client breaks it, and its a headache trying to debug, because EVERYTHING is dynamic. There is no way we can rollback the earlier change, because then the other client would be screwed.

Its not done in a real 3-tier architecture - its a web site with references to a DB class, and a class library. There is business logic in the web site itself, in the class library, and the database stored procs (Validation is done in the stored procs).

I've been put in charge of re-organizing the whole thing, and these are my thoughts/questions:

  1. I think this is a bad model in general, because one of the things I heard one of the developers say is that anytime any client makes a change, we should deploy to everybody - but that is not realistic, if we have say 20 clients - there will need to be regression testing on EVERYTHING, since we don't know the impact...

  2. There are about 100 forms in total, and their is some similarity in them (not much). But I think the idea that a dynamic engine can solve ALL form requests was not realistic as well. Clients come up with the most weird requests. For example, they have this engine doing a regular data entry form AND a search form.

  3. There is a lot of preserving state between pages, and it is all done using session variables, which is ok, except that it is not really tracked, and so sessions from the same user keep getting overwritten, and I think sessions should be got rid of.

  4. Should I really just rewrite the whole thing? This app is about 3 years old, and there has been lots of testing and things done, and serious business logic implemented, so I hate to get rid of all that (joel's advice). But its really a mess of a sphagetti code, and everything takes forever to do, and things break all the time because of minor changes.

I've been reading Martin Fowlers "Refactoring" and Michael Feathers "working effectively with legacy code" - and they are goo开发者_C百科d, but I feel they were written for an application that was 'slightly' better architected, where it is still a 3-tiered architecture, and there is 'some' resemblance of logic..

Thoughts/input anyone?

Oh, and "Help!"


My current project sounds like almost exactly the same product you're describing. Fortunately, I learned most of my hardest lessons on a former product, and so I was able to start my current project with a clean slate. You should probably read through my answer to this question, which describes my experiences, and the lessons I learned.

The main thing to focus on is the idea that you are building a product. If you can't find a way to implement a particular feature using your current product feature set, you need to spend some additional time thinking about how you could turn this custom one-off feature into a configurable feature that can benefit all (or at least many) of your clients.

So:

  1. If you're referring to the model of being able to create a fully customizable form that makes client-specific code almost unnecessary, that model is perfectly valid and I have a maintainable working product with real, paying clients that can prove it. Regression testing is performed on specific features and configuration combinations, rather than a specific client implementation. The key pieces that make this possible are:
    1. An administrative interface that is effective at disallowing problematic combinations of configuration options.
    2. A rules engine that allows certain actions in the system to invoke customizable triggers and cause other actions to happen.
    3. An Integration framework that allows data to be pulled from a variety of sources and pushed to a variety of sources in a configurable manner.
    4. The option to inject custom code as a plugin when absolutely necessary.
  2. Yes, clients come up with weird requests. It's usually worthwhile to suggest alternative solutions that will still solve the client's problem while still allowing your product to be robust and configurable for other clients. Sometimes you just have to push back. Other times you'll have to do what they say, but use wise architectural practices to minimize the impact this could have on other client code.
  3. Minimize use of the session to track state. Each page should have enough information on it to track the current page's state. Information that needs to persist even if the user clicks "Back" and starts doing something else should be stored in a database. I have found it useful, however, to keep a sort of breadcrumb tree on the session, to track how users got to a specific place and where to take them back to when they finish. But the ID of the node they're actually on currently needs to be persisted on a page-by-page basis, and sent back with each request, so weird things don't happen when the user is browsing to different pages in different tabs.
  4. Use incremental refactoring. You may end up re-writing the whole thing twice by the time you're done, or you may never really "finish" the refactoring. But in the meantime, everything will still work, and you'll have new features every so often. As a rule, rewriting the whole thing will take you several times as long as you think it will, so don't try to take the whole thing in a single bite.


I have a number of similar apps for building dynamic forms that I support.

There's a whole lot of things you could/could not do & you're right to think hard before throwing away 3 years of testing/development.

My input for you to consider is to implement a plug-in architecture on top of what you're got. Any custom code for a form goes in the plug-in & the name of this plug-in is stored with the form. When you generate a form, the correct plug-in is called to enhance the base functionality. that way you get to move all the custom code out of the existing library. It should also mean less breaking changes, each plug-in only affects the form it's attached to.

From that point it'll be easy to refactor the core engine as it's common functionality across all clients & forms.


Since your application seems to have become a big ball of mud, a complete (or an almost complete rewrite) might make sense.

You should also take into account new technologies like document-oriented databases (couchDB, MongoDB)

Most of the form definitions could probably fit pretty well in document-oriented databases. For exemple:

To define a customer form, you could use a document that looks like:

{Type:"FormDefinition",
 EntityType: "Customer",
 Fields: [
   {FieldName:"CustomerName", 
    FieldType:"String", 
    Validations:[
      {ValidationType:"Required"},
      {ValidationType:"StringLength", Minimum:15, Maximum:50},
    ]},
    ...
   {FieldName:"CustomerType", 
    FieldType:"Dropdown", 
    PossibleValues: ["Standard", "Valued", "Gold"],
    DefaultValue: ["Standard"]
    Validations:[
      {ValidationType:"Required"},
      {
        ValidationType:"Custom", 
        ValidationClass:"MySystem.CustomerName.CustomValidations.CustomerStatus"
      }
    ]},
    ...
 ]
};

With this kind of document to define your forms, you could easily add forms and validations which are customer specific.

You could easily add subforms using a fieldtype of SubForm or whatever.

You could define FieldTypes for all common types of fields like e-mail, phone numbers, address, etc.

namespace System.CustomerName.CustomValidations {
  class CustomerStatus: IValidator {

    private FormContext form;
    private List<ValidationErrors> validationErrors;

    CustomerStatus(FormContext fc) {
      this.validationErrors = new List<ValidationErrors>();
      this.form = fc;
    }

    public List<ValidationErrors> Validate() {
      if (this.formContext.Fields["CustomerType"] == "Gold" && Int.Parse(this.form.Fields["OrderCount"]) < 10) { 
        this.validationErrors.Add(new ValidationError("A gold customer must have at least 10 orders"))
      }

      if (this.formContext.Fields["CustomerType"] == "Valued" && Int.Parse(this.form.Fields["OrderCount"]) < 5) { 
        this.validationErrors.Add(new ValidationError("A valued customer must have at least 5 orders"))
      }
      return this.validationErrors;          
    }
  }
}

A record of a document with that definition could look like this:

{Type:"Record",
 EntityType: "Customer",
 Fields: [
   {FieldName:"CustomerName", Value:"ABC Corp.", 
   {FieldName:"CustomerType", Value:"Gold",
   ...
 ]
};

Sure, this solution is a lot of work, but if/when realized it could be really easy to create/update/customize forms.


This is a common but (IMO) somewhat naive design approach. "Instead of solving the customer's problem, let's build a tool to let them solve their own problems!". But the reality is, that generally customers want YOU to solve their ACTUAL problems. So build things that solve their problems. If you can architect it in a way that allows you to reuse some parts for different customers, fine. But that is generally what the frameworks have done for you already - work out the common features that applications need and make them available in neat packages.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜