Skip to content

Data and MetaData Objects

Steve Ives edited this page May 26, 2020 · 2 revisions

Harmony Core Logo

A DataObject is the fundamental building block in Harmony Core. DataObjects are usually generated by CodeGen from a structure defined in a Synergy repository and they inherit from Harmony.Core.DataObjectBase. DataObjects are generated with metadata that communicates the size, type, and location (position in the record) of each of your fields to other parts of Harmony Core. You can read a more in-depth exploration of DataObject here.

Data Objects

In this topic:

Data Objects and Their Classes

Data objects are the fundamental elements of Harmony Core. Each data object represents a Synergy record and is defined by a class that mirrors the repository structure for that record. One of the main functions of the code in these classes is to manage Synergy data internally while exposing field properties as CLS types, which are useful in both Synergy applications and a wide variety of non-Synergy applications.

Data object classes are typically generated using a CodeGen template included in the harmonycore project template (see Generating a Data Object Class below). Each data object class is generated from a structure in a Synergy repository and inherits from Harmony.Core.DataObjectBase, which implements the System.Icomparable interface methods that enable data objects to be sorted. Generated object classes are optimized to ensure that these comparisons are as fast and type-accurate as possible. Additionally, each class wraps Synergy data so it can be efficiently serialized and deserialized, keeps track of whether data has changed, and stores the GRFA for the record when the data was accessed. (GRFAs are critical for most Entity Framework (EF) Core operations and are useful for implementing optimistic concurrency.) Metadata associated with data objects enables instances to be created quickly from Synergy data.

Each generated data object class invokes Harmony Core converters that convert data object properties to and from underlying repository structure types. When building Synergy .NET application components, you have access to the entire Microsoft .NET Framework. However, Synergy types such as alpha, decimal, and implied decimal are unknown to this framework, so converters (which are classes in the Harmony.Core.Converters namespace) provide the ability to convert between standard Synergy types and .NET types. For example, converters convert Synergy alpha data to .NET String data (and vice versa), and convert Y/N fields to Boolean data (a common requirement for legacy code). Along with these basic types of converters, there are converters that handle string formatting—e.g., that convert Synergy decimal data to DateType data and vice versa.

Validation

Data object validation is enabled by overriding the Validate method for DataObjectBase. Because data object classes are typically generated, it is important to implement validation in a partial class in a file you’ve added to the Services.Models project (e.g., Services.Models\MystructureCustom.dbl). Validate has the following signature:

public virtual method Validate, void
    type, ValidationType
    serviceProvider, @IServiceProvider

Note that Validate takes a System.ComponentModel.IServiceProvider, which can be used to get access to the following, among other things:

  • Harmony.Core.FileIO.IFileChannelManager
  • Harmony.Core.Context.IDataObjectProvider
  • Harmony.Core.Interface.IDataObjectTransactionContext

The first two (IFileChannelManager and IDataObjectProvider) are useful when you need to read from an ISAM file to perform validation steps. IDataObjectTransactionContext can be used to validate properties in reference to other existing objects—e.g., to check all the data objects currently being modified to ensure some other object is also valid or is in some way related.

Your code should throw an exception for invalid objects. By convention, this should derive from System.ComponentModel.DataAnnotations.ValidationException. For example:

import System
import System.Collections.Generic
import System.ComponentModel.DataAnnotations
import System.Text
import Harmony.Core
import System.Diagnostics
import Microsoft.Extensions.DependencyInjection
import Harmony.Core.Interface
import Harmony.Core.Context
. . .
public partial class Customer extends DataObjectBase
. . .
public override method Validate, void
    type, ValidationType
    serviceProvider, @IServiceProvider
proc
    ;For this example, we’ll get dbContext, IDataObjectProvider, and 
    ;IDataObjectTransactionContext service objects for dependency injection.
    data dbContext = serviceProvider.GetService<DbContext>()
    data dataObjectProvider = serviceProvider.GetService<IDataObjectProvider>()
    data fileChannelManager = dataObjectProvider.ChannelManager
    data transactionContext = serviceProvider.GetService<IDataObjectTransactionContext>()

    if(dbContext == ^null || fileChannelManager == ^null || transactionContext == ^null)
        throw new Exception("Something was null") 

    ;We’ll use tracing (System.Diagnostics.Trace) in this example to display 
    ;a message when validation occurs:   
    Trace.WriteLine(string.Format("Validating Customer Object for operation type {0}", type))

    ;Simple validation:
    if(this.City != "Tbilisi")
        throw new ValidationException("City was not valid")
endmethod

Note the following:

  • System.Data.Entity.DBContext cannot be used in validation operations because EF Core does not support recursive operations.
  • Validation will occur during the DBContext.SaveChanges operation prior to locking/writing/deleting operations.

Out-of-Band Object Loading

For calculated fields or complex multi-part records that do not fit the EF Core relational model, you can override the LoadOwnedObjectsInternal method for DataObjectBase:

protected virtual method LoadOwnedObjectsInternal, void
    serviceProvider, @IServiceProvider
proc

This method takes an IServiceProvider that can be used to retrieve an IFileChannelManager or IDataObjectProvider service object. Using these services, you can efficiently load whatever data you might need from ISAM files. You can register additional services during DBContext.OnConfiguring.

LoadOwnedObjectsInternal is called during the EF Core provider’s materialization phase, just prior to registering the object with the EF Core state tracker.

Data Object Metadata

Synergy record data is kept separately from the metadata generated for the record. This reduces the size of each data object and keeps the cost of object construction low. Harmony.Core.DataObjectMetadataBase contains the lookups necessary to enable EF Core query models to be converted to Synergy Select operations.

DataObjectMetadataBase also acts as a factory for DataObjectBase derivatives. The ODataModel.tpl CodeGen template creates a static constructor for each data object that registers its metadata against a static type lookup in DataObjectMetadataBase. This enables generic I/O handling classes like File I/O and the Harmony Core Entity Framework Provider to work without using reflection, even though those classes have no information on the types they will encounter until runtime.

Generating a Data Object Class

A data object class can be generated using CodeGen with the ODataModel.tpl template, which generates the class from a Synergy repository structure. This CodeGen template is included in solutions created from the harmonycore project template (see Code Generation), and the regen.bat file (also included in the harmonycore template) is set up to automatically run CodeGen with the template. However, you can create your own CodeGen commands for this. For example, the following is for a structure named CUSTOMER:

codegen -s CUSTOMER -t ODataModel -n Services.Models -o Services\Models -r
Clone this wiki locally