NetQuarry adds Master Page Support, Google Chrome fix

The v2.2.1 release of the NetQuarry platform includes support for .Net Master Pages.  The release also includes a new frameless presentation format that takes advantage of the Master Page support.  This new consumer-oriented presentation, referred to the Single Frame Master format, provides an alternative to the platform’s existing enterprise-oriented presentation format.  The new format has been in beta release for several months.

The timing of this release was also an opportune time to release a workaround for a significant Google Chrome 2.0 bug that causes HTML tables with certain CSS styling to render without visible content.

Also included are a number of significant fixes and UI enhancements.

TypedMappers

A TypedMapper is a helpful, extrememly lightweight type-safe wrapper class around a NetQuarry Mapper object.

You typically use a TypedMapper as a helper class in an extension. You can create a TypedMapper and attach it to the sender (IMapper) of the event or you can use the TypedMapper derivative to create a new mapper – either for the purpose of creating a new row or reading/updating an existing one. Connecting a TypedMapper via one of the constructors or TypedMapper.Attach is very inexpensive and generally a safer way to work with a mapper.

The TypedMapper is an abstract base class, which means that you have to use our generator to generate derived classes that wrap your mappers. The ONLY potential drawback to using a TypedMapper derivative vs. the Mapper as is is that you will need to update (re-generate) the classes as you make metadata changes. On the applications that we (NetQuarry) are personally managing we are putting the generation code after we save the metadata and check-in the class file. We are recomending that you keep the generated classes more or less stand-alone (e.g. in its own assembly) and without any dependencies on other code.

TypedMapper objects that have custom functionality added to them should all be placed in the Data project. This allows that functionality to be shared with other consumers. You can add TypedMapper objects anywhere in your code base and add different functions to each instance. That is not recommended.

You create a typed mapper object by deriving the class from the equivalent generated template typed mapper object. The template object gives you all the type safe declarations for fields on the mapper that are on the mapper when flavor 0 is applied to the mapper. All fields with an include flavor are not in a generated TypedMapper.

To declare a typed mapper, you derive your class from the generated class template:

public class People : Comensura.Data.Generated.people<People>
public class CompaniesTemplates : Comensura.Data.Generated.companies_templates<CompaniesTemplates>

Or, more generally:

public class TName : Application.Data.Generated.TBase<TName>

You give your typed mapper class the name almost exactly derived from the generated class name, removing any underscores and proper casing the letters for legibility. Your template object is your class. Then just treat your object like any other class and add public and private methods as appropriate.

Code in Extension or TypedMapper?

A commonly asked question is where to put your business logic. The temptation is to add business logic to Mapper extensions. Well, the basic rule of thumb is that only decision/workflow/UI logic is put in the extension and data manipulation/business logic is performed in the TypedMapper object.

Having said that, there is nothing inherently wrong with putting data manipulation code in an extension as long as it’s self contained.

If you add data manipulation code that is likely to be shared then you should probably think about putting that code as a method on the TypedMapper object. The alternative to sharing code via the TypedMapper is to share the code in the Common class, but as a static method.

So, back to the TypedMapper object. Remember a TypedMapper is just a mapper and whenever you call your TypedMapper functions, your logic is going to read and write data from the current row. Of course there are many things you can do in your functions but they all relate to some operation controlled or directed by the values in the current row.

NetQuarry Wizard Enhancements

September 24, 2007 – NetQuarry, Inc. announces release of the first-generation meta-data-driven wizard engine for its NetQuarry Enterprise Application Platform.

NetQuarry’s new wizard engine is capable of composing wizard pages driven entirely by declarative meta-data. NetQuarry application developers can take advantage of the new wizard engine to add multi-page data collection processes to their applications without writing a single line of code. Like other NetQuarry rendering components, the wizard engine allows developers to customize their application even further by adding programmatic extensions to the wizard.

Earlier versions of the NetQuarry wizard engine required developers to create custom user controls, configured via meta-data into the application, for each page in each wizard. The upgraded wizard engine retains this ability, but adds the ability to create entire wizards in a purely declarative manner.

NetQuarry Chief Scientist Campbell Woods described the idea behind the new functionality this way – “The idea behind the wizard engine improvements is really consistent with one of the design principles we try to incorporate in the platform as a whole. That is, try to make it very easy to do the things developers do all the time and still allow them to reasonably do the one-off things that every application needs.”

About NetQuarry

NetQuarry develops and licenses the NetQuarry Enterprise Application Platform. The NetQuarry platform helps professional software development teams develop significantly better enterprise and hosted applications.

NetQuarry is privately held. For more information, call (714) 881-4574, email info@netquarry.com, or visit our website at www.netquarry.com.

Picklists

We’ve spent a bunch of time solving the problem of vocabulary lists for a database application. A vocabulary list is one of those tables that have a key (typically an integer or a GUID) and some text. The “key” (primary key in DBMS-speak) is referenced by a column in a more important table. Most of the time, the end-user selects this key by choosing an item via a drop down list box that has some human readable text and the application stores the item’s “key” into the database. We call the object that solves this problem a Picklist.

Normally, you solve the problem by creating a 2 column table with a key and some descriptive text. For example, say you wanted to have a status table:

Table: status

StatusID StatusText
1 Open
2 Closed

You might get a stab of reusability and conclude that a good idea is to store these sorts of things in a table that has a type and therefore keep yourself from having to create 300 of these tables in your database. So, the table might look like this:

Reusable design: picklist

id description list_type
1 Open status
2 Closed status

Now, all you have to do is change the SELECT statement and you can have one table to handle most everything. Next, you bump into a requirement where you have to store something besides and integer as the key (say, its data that you didn’t own and the key is some cryptic string). No problem, you just add another column to this list with an alternate ID (or another table, I suppose) and indicate that somehow.

2nd try: picklist

id text_id description list_type
1 OP Open status
2 CL Closed status

Next, you decide that some of the items in this list are required to be shown, but you don’t want the user to select them. So you add another column to the table and indicate which items are disabled.

3rd try: picklist

id text_id description list_type disabled
1 OP Open status 0
2 CL Closed status 0
3 PO Nearly Closed status 1

At this point you’ve thought of several more interesting requirements so your table design ends up with several more columns to support these:

  • Localization. If you plan to support a multi-lingual application you are very likely to need to have your vocabulary displayed in the user’s culture.
  • Readable string as a way to lookup an ID. The common case for this is when you want either to set a default value in a field but do not want to hard code some opaque primary key values in your code or you want to take some action based on the value that a user chooses and you don’t want to hardcode the primary key value.
  • Caching (or not) the items in the list. If you are going to model this, you’ve likely decided that you need to metadata tables – Picklist (for the name of the list and some attributes like “cache”), and PicklistItems (for the actual items).
  • Discrimination. Often you’ll have a requirement where the value in one column filters (or discriminates) the potential values in another. For example, you may have a list of vehicle make types in one list and based on the selection, you need to show the available models in another. The typical way to solve this is to take the value from the first control, post the results to the server, dynamically determine the new SELECT statement, and return the page with the second control filtered.
  • Two-way lookup. Often you have the value of the item’s key only and you need to find the text, but we have found that you just a frequently need to do the reverse (e.g. you have the text and you want the key). We use the reverse lookup, for example, when searching or filtering in a list – the user enters “Open” and we search for “1.”

The next issue you solve is how to fill the controls with the contents of these lists. The most straightforward method is to write code to open a DataReader with a statement like (SELECT id, description FROM picklist WHERE list_type = ‘status’) and add these items to a DropDownList control like this:

while (dr.Read())
{
    listControl.Items.Add(new ListItem (dr.GetString(0), dr.GetInt32(1)));
}

Or, a better way is to bind using the DataBinding features of the DropDownList– something along these lines:

DataTable dt = CreateTableFromPickListSQL();
dropList.DataSource = dt;
dropList.DataTextField = “id”;
dropList.DataValueField = “description”;
dropList.DataBind();

In both cases, you are responsible for creating either the DataReader or DataTable and properly disposing of it. Most folks know how to do this, or at least they know how to cut and paste some code somewhere that does it reasonably well. It’s still a pain and generally isn’t written in such a way to promote even moderate reusability.

Another consideration is that you may need to use one of these lists for something other than as a DataSource for a DropDownList control. For example, if you are displaying a table of data that has several columns which are foreign keys to your picklist table you either must create some sort of view to resolve these or do a lookup as you fill the list (this is almost never an option for performance reasons). The trouble with the view is that unless you strictly enforce the integrity of your data you are likely to lose some rows because the values in your main table have foreign key values that are invalid.

There are a bunch of other factors, of course, which is why there is a dedicated object to solve this problem. Again, a Picklist represents a domain specific list of values that can be used to translate the key of an item (typically a foreign key in a database table) to a description.

In the NetQuarry Platform, Picklists are of one of three distinct types:

  • A “standard” Picklist. This type of list is loaded from the metadata and supports the descriptive text in the current user’s culture (language). We use a pattern similar to the one just described to model this. (Picklist, PicklistItems (with a foreign key to a “Text” table to support localization))
  • A “SQL” Picklist. This type of list is loaded from one of the databases (can be the “main” operational database or the repository) and can contain 1 to 3 columns of data.
  • An “Enumeration” Picklist. This list is a 2 column Picklist loaded from a string type name that represents an Enumeration as its source. (The NetQuarry Studio makes good use of this.)
  • For the most part, Picklists tend to have the LimitToList attribute set. This attribute says that the item selected by the user MUST be one of the items in this list. There are several other attributes of interest as well, listed here and briefly explained:

 

Member Name Description
Cache This list should be cached.
HasDiscrim This list uses a discriminator to break items into sets.
LimitToList This list has at least 2 columns and the values must come from the list.
HideUnknownItems The list should hide unknown items.
MarkUnknownItems The list should mark unknown items.
StoreAltText The list stores the item”s Alternate Key Text column in the DB and uses it as it”s ID.
StoreAltInt The list stores the item”s Alternate Key Int column in the DB and uses it as it”s ID.
StoreItemName The list stores the item”s Name column in the DB and uses it as it”s ID.
NoNullEntry The list does not need to provide a null entry.
SortByText List should be sorted by the display text.
SortDesc List should be sorted in DESC order. (Standard Picklists only)
SortByKey List should be sorted by the stored key.
AllowUserAdd Users can add new items to this list (providing they have Configuration permission).
AllowUserDelete Users can remove items from this list (providing they have Configuration permission).
AllowUserUpdate Users can update items in this list (providing they have Configuration permission).
Disabled The picklist is disabled.
IgnoreWhitespace Ignore leading and trailing whitespace when matching values.