mv.NET is a suite of three packages, one of which is the Adapter Objects library. This allows MV data to serve as a data source for ADO.NET just like any relational data source. Using the library can be very easy. In Visual Studio you can drop toolbox objects onto a form. Doing so invokes a Wizard to configure the components. Wow, that’s neat and it all works – but is this good coding practice?
The mv.NET Data Adapter Configuration Wizard really does create some good code. You start by selecting controls from the toolbox as shown in the first image on the left here, and you end up with controls at the bottom of your form as we see in the second image on the right.
In VS2005 with .NET 2.0 our actual form code isn’t touched. Unlike VB6 and VS2003, information about components isn’t stored in the actual form code. .NET 2.0 makes use of "partial classes" which allows a single class to be broken into many files. These are transparently assembled at compile time to create the final definition. VS2005 puts all of its generated code, including anything created by mv.NET, into a separate file called FormName.Designer.xx, where .xx is either .vb or .cs depending on whether you’re using VB.NET or C#. The code maintained by the developer has a simple constructor that calls InitializeComponent, which is what was generated by the IDE. So outside of this, only the developer’s code is contained in the more generic FormName.vb or FormName.cs item.
What we’re seeing is that mv.NET generates code for non-visual components, but the resulting data access code is stored in the .Designer page of a form. That’s really mixing the UI with the data definitions in a very non-MVC way! Unfortunately, I don’t think there are many options here. We could copy all of that good code to another module after it’s been generated. I’ve manually written plumbing code to use Adapter Objects and the code looks almost the same as what gets generated. Having gone through the exercise, personally I’d much rather take advantage of the benefits of the wizard compared to doing a lot of hand coding.
The problem is that the wizard-generated code is simply in the wrong place. The code can’t be re-used anywhere else because it’s intermixed with definitions for visual components on that specific page. To change the components it’s very easy to re-invoke the wizard or to use property pages for the various components. This means configuration changes can be made quickly and with no change to code/syntax. But those visual helper tools modify the data in the .Designer file. If the code is extracted from that file then we lose the benefit of the configuration tools.
Note that this is not a fault in mv.NET. We’ve chosen to use the Visual Studio IDE. This is how it works and this is how other products with wizards work. The property pages visible on a form don’t work on non-visual components (at least to my knowledge – Set me straight if I’m wrong!). Maybe this is an enhancement they’ll make to VS in the future where the object being manipulated in a property page can be any object defined in a code module, and custom property pages can overload the defaults. (Hmm, I wonder if this would make for a good VS add-in…)
At this point, here’s my plan. Use the tools that are appropriate at a given time but switch tools when they no longer apply. Until I find compelling reason to do otherwise, I’m going to continue to use the Data Adapter Confituration Wizard to create this specific code for me but when I find it’s becomming more limiting than helpful I’ll have to stop using it to continue maintenance of established code. In other words, when I get things working exactly the way I want within a form, ADO.NET, and mv.NET, I’ll refactor the data access code into it’s own module and get the Form_Load code to call my code rather than leave such things to the form initialization routine. Another advantage of this is that I probably don’t want data access to initialize on form load anyway, but when the user decides they want to do something that requires data, so in the end this sort of refactoring after code generation might be inevitable. Another advantage is that such code can be re-used elsewhere and even put into a specific data access (business object) class which can then be instantiated in other projects. The reality is that code like this is pretty specific to a form/application, but it could serve as a good start elsewhere.
For people who care more about functionality than code structure, elegance, etc, I’m sure none of this is a big deal. As long as the code works, who cares? That’s true until you realize that you will need to go through the Wizard all over again in each form that incorporates similar logic. If you wind up changing tools for some reason you’ll again find it wasn’t a good idea to hard-code your data access code into your form. This is a lesson that’s learned the hard way by many developers – my recommendation is to learn from other people’s mistakes and separate your UI from data right from the beginning.