This article discusses a way to approach model-based development with ASNA Visual RPG. This type of coding has been around for a long time. Some of you may recognize some of the concepts presented here from our past advanced AVR classes. However, as you’ll soon see, using data models usually requires tooling to generate the models–and that often limits its use with AVR.

Generally, with AVR, you don’t need to use a model-based approach (or at least a "pure" model-based approach). The AVR Memory File/DataSet model is an effective stand-in for pure modeling in many use-cases (and virtually all of them in a 100% AVR vacuum). Lately, though, we’ve helped several customers integrate AVR with C# (usually using .NET’s MVC Web app model). When merging AVR and C# in the same project, the language boundaries start to reveal weaknesses with AVR Memory File-based IO. This is especially true when AVR programming teams use AVR to fetch data from the IBM i to pass it off to C# MVC coders. 

Not only does the DataSet-approach present technical barriers, but most C# coders get judgy, indignant, and cranky if you ask them to use a DataSet. 

While the techniques presented here are especially effective in a polyglot programming environment, you may also want to use them in your 100% AVR projects.

For years, AVR programmers have used the Visual RPG Memory File to populate user interfaces, especially grids, in both Windows fat client apps and in ASP.NET Web apps. Even beginning AVR coders are familiar with the general pattern of using a Memory File. To populate a customer grid (in either fat clients or Web apps), you read records from a file, write them to a memory file, and bind the resulting DataTable to a grid as its DataSource.

For example, the code below populates a DataGridView in a fat client with a Memory File.

Figure 1a. Code to populate a DataGridView using a Memory File

A GitHub repo with the example code this repo references is available at this GitHub repo.

The code above produces a Windows form that looks like this:

Figure 1b. The Win form produced with the code in Figure 1a. The code above isn’t complete. It arbitrarily reads 14 rows into the grid. A production-ready program needs logic to page through the data. However, the code above is quite representative of a basic Memory File pattern that hundreds of AVR coders have used in AVR apps for many, many years. A variation on the pattern above might use a program-described Memory File. In this example, the Memory File is described by the file specified in its FileDesc keyword–which makes the underlying DataTable have exactly the same structure as the DclDiskFile’s data file. The AVR Memory File is essentially a wrapper around the System.Data.DataSet object. The creamy nougat center of a DataSet is a System.Data.DataTable. The DataTable is analogous to a member in an IBM i data file. The DataTable is the data container–it is where rows written to the Memory File reside. The code below (from the example above) assigns the rows in the Memory File’s DataSet’s zeroth DataTable (which with the pattern above is the only DataTable in the DataSet) as the data source for the DataGateGridView.

The strength of this Memory File/DataSet/DataTable approach is its simplicity and ease. With a 4-line Memory File declaration and about 20 lines of code, you can populate a grid in either a fat client or a Web app. Let that soak in. How many lines of code does it take to populate a subfile with ILE RPG? Fuhgeddaboutit! The schema shown below in Figure 2a defines the data file used in Figure 1a.

Figure 2a. The file layout for the data file used in Figure 1a. The Memory File in Figure 1a automatically creates a buffer (the DataTable) based on the file layout. With field names in the buffer identical to that in the data file, this enables AVR to read from the data file and populate a Memory File easily:

For all intents and purposes, the Memory File/DataSet/DataTable approach is a model-based approach, with the underlying DataTable being the model. The DataTable layout from Figure 1a is exactly as you see in Figure 2a’s file layout. Don’t let anyone tell you that DataSet/DataTable-based coding is bad. It’s not. You can create supremely clever business solutions with this model.

A model-based approach

That said, time and technology march onward. Nearly all Microsoft tutorials lately eschew the DataSet/DataTable-based model for a more pure, model-based approach. They did that because DataTables are not strongly typed–and therefore their contents are not discoverable at runtime by .NET. To get data out of a DataTable, you have to explicitly interrogate its ItemArray property and you have to cast the weakly typed value like this:

A model-based approach is discoverable by .NET and is strongly typed. Intellisense proves this; you can see and work with the contents of a strongly typed object at development time.

The MVC model does a similar discovery at development time and, perhaps even more importantly, at runtime (using reflection), for tasks like populating a Web page at runtime. Let’s get a few more model basics under our belt. What does a model-based approach mean? It means that a “model,” created with code, defines the data buffer needed. For example, a way to code the model for the file defined in Figure 2 with ASNA Visual RPG, is shown below in Figure 2b. (Columns are aligned to make it easy to compare Figure 2a and Figure 2b.)

Figure 2b. An AVR custom model to define the file shown in Figure 2a. Dispense for a few minutes with the realization that to manually create a buffer like this for a 100 (or 500! We’ve seen ’em!) field file would be an excruciating job. You need some kind of tooling (a fancy word for code generator) to produce accurate and reliable data models. For now, let’s pretend like you had a magic wand that could create a buffer like this for any file of yours in about 2 seconds (no fair peeking ahead!)

You’ll probably quickly notice that the data types from Figure 2b are different from those in Figure 2a. Or are they? They are not, AVR’s *Packed data type is simply an alias for .NET’s System.Decimal type. You can use either in your AVR code. See the table here for a list that maps AVR data types to .NET data types.

Getting a list of models

A model-based approach needs two things: the model, as shown above. But it also needs a list of models, for data binding to a grid, passing many data rows from a Web service, or many other things. For that, a couple of approaches are possible. One thing you can do is create an array of the CustomerModel class, like this:

See this link if you aren’t familiar with ranked arrays.

An array of models is often a good approach, but it’s not easy to add elements to an array on the fly. Rather than use an array we’ll use a .NET generic collection. In this case, we’ll use a List of <T> also known as List<T>. (In your head say “List of tee”). Looks weird, right? Don’t let it scare you, the <T> is shorthand for “a specific type.” The List<T> object lives in .NET’s System.Collection.Generic namespace. The following AVR code defines a list of CustomerModel objects:

Figure 3. Declaring a strongly typed list of the CustomerModel object.

Watch your parentheses when using .NET’s generic objects with AVR. They look a little obtuse at first.

You can add only instances of CustomerModel to this list. Attempting to add any other object type causes a compile-type failure.  In much older AVR code, you may be familiar with .NET’s ArrayList object. It is similar to the List<T> object, but ArrayList isn’t strongly typed. The ArrayList is an array of System.Objects. Therefore, you can add objects of any type to the same ArrayList–and getting an element out of the ArrayList requires casting the object (as shown below in Figure 4.)

Figure 4. Using the weakly-typed ArrayList. Weakly-typed lists won’t work with C# and its standard MVC databinding. For that, we need the strong typing that List<T> provides.

Putting it all together

How does it all work? The AVR code below uses the CustomerModel object as an alternative to the Memory File. It populates a Windows form and looks exactly like the form shown in Figure 1b. Figure 5. AVR code using the CustomerModel objects While this is a 100% AVR example, it would be easy to pass its Customers list (declared locally in this example in the ReadRows method) to C#. That list would look exactly like a C# would hope it would in an MVC application. The code in Figure 5 further reveals the need for tooling. Producing the routine to populate a customer model (shown below in Figure 6) is a pretty simple job for a demo file. But for complex, big production models, trying to create the code you need by hand is quite challenging (and probably not really doable).

The tooling

A model-based approach requires tooling to generate code for you. Microsoft provides such tooling in .NET’s Entity Framework. Many other popular Web frameworks in other languages also provide such tooling. To help bootstrap you into model-based coding, CreateDGDataModel is an AVR console application that creates a model class for a given table. To be clear, this is a hack tool of the highest order! It grew out of needing several models for a very specific customer project. Time was tight and we needed many large models to be consumed by C# and an MVC application. Despite its hacky nature, the tool makes possible what otherwise probably isn’t a rational pursuit by hand. CreateDGDataModel is run from the command line.
It requires four arguments:
  1. -d or –database-name
  2. -l or –library-name
  3. -f or –file-name
  4. -c or –class-name
Using the same table as described in Figure 2a, CreateDGDataModel produces this source: Figure 6. Code produced by CreateDGDataModel The generated code is placed on the clipboard, for pasting directly into your project. CreateDGDataModel creates two important chunks of code:
1. It produces the model definition
This provides the main body of the data model–the file buffer.
2. It produces model “setter” and “getter” routines
It’s not enough to produce the buffer, you also need routines to move data to a model and move it from a model. These two routines do that. Alas, because of the global nature of an AVR DclDiskFile, there isn’t an effective way to pass a reference of the record format to these routines. You’ve got two choices to resolve this: either copy the “getter” and settings to your AVR class where the DclDiskFile is declared or use this class as a bootstrap class and the DclDiskFile and the necessary file IO routines to the class. I told you it was hacky! But for AVR coders needing to move data to and from C# for MVC (and other) uses, it is quite serviceable. As I said at the beginning, a model-based approach isn’t necessary to be highly productive with ASNA Visual RPG. The proven, and relatively simple, Memory File/DataSet model does a great job within the boundaries of AVR. However, if you’re crossing language barriers working with a C# team or using some C# yourself, sometimes you need to use a model-based approach. Now you can!