The import/export activity

Importing and exporting data is most likely something that you have to do more than once. It can be nice to have your own module for testing purposes.

I know that the standard import/export tool in AX also lets you do many of these things, but you are a programmer, right? So you might prefer to write the code yourself.

Anyway, I just wanted to get you started with a simple example that shows how to build a simple dialog where the user can select what to import/export, the filenames, and so on.

Here is the class model diagram for the example:

The import/export activity

The ImpExpFileDialog class

This is the dialog class that is common for all the different import/export entities. You do not need to change this class when adding a new entity class. Change this class only if you would like the dialog to look different or want to add additional fields to the dialog. The methods in this class (and the rest of the classes in this example) are explained by header comments.

The classDeclaration method

This class provides the starting point for the ImpExpFile classes by presenting a dialog for the user, creating an object of the ImpExpFile class hierarchy, and then passing the control to the created object. The following code shows you how to implement it:

class ImpExpFileDialog extends RunBase,
{
  // Global variables
  FileName            fileName;
  FileType            fileType;
  FileEntity          entity;
  ReadWrite           readWrite;
  Dialog              dialog;
  // Dialog fields
  DialogField         dialogFileName;
  DialogField         dialogFileType;
  DialogField         dialogEntity;
  DialogField         dialogReadWrite;
  #define.CurrentVersion(1)
  // Macro that contains the global variables
  // to be kept until the next time the user
  // executes this class.
  #localmacro.CurrentList
    fileName,
    fileType,
    entity,
    readWrite
  #endmacro
}

The dialog method

The dialog method defines the dialog that is presented to the user when the prompt method is executed. The following code shows us how the dialog method is implemented:

public Object dialog()
{
  // Create a new object of the DialogRunbase class
  dialog = new DialogRunbase("Import data from file", this);
  // Add the fields to the dialog and
  // set the initial value to be equal to
  // the value selected last time.
  dialogReadWrite = dialog.addFieldValue(typeid(ReadWrite), readWrite);
  dialogFileName = dialog.addFieldValue(typeid(FileName), fileName);
  dialogFileType = dialog.addFieldValue(typeid(FileType), fileType);
  dialogEntity = dialog.addFieldValue(typeid(FileEntity), entity);
  return dialog;
}

The getFromDialog method

This method, originated from the RunBase class, is initiated when the user presses OK in the dialog, and it is used to store the user-selected values. The following code shows us how the getFromDialog method is implemented:

public boolean getFromDialog()
{
  fileName        = dialogFileName.value();
  fileType        = dialogFileType.value();
  entity          = dialogEntity.value();
  readWrite       = dialogReadWrite.value();
  return super();
}

The pack method

The pack method is a standard method inherited from the RunBase class. It is used to save the values that the user selects and store them until the next time the user executes the class. The following code shows us how the pack method is implemented:

public container pack()
{
  return [#CurrentVersion,#CurrentList];
}

The run method

The standard methods from the RunBase class are typically used to start the main execution flow of the class. The following code shows us how the run method is implemented:

public void run()
{
  // Create a new object of the ImpExpFile class-hierarchy
  // based on the values selected in the dialog.
  ImpExpFile      impExpFile = ImpExpFile::construct(fileType, fileName, entity, readWrite);
  // Start the main flow of the ImpExpFile object
  impExpFile.run();
}

The unpack method

It is the standard method inherited from the RunBase class and is used to fetch the values that the user selected the last time he executed the class. The following example shows us how the unpack method is implemented:

 */
public boolean unpack(container packedClass)
{
  Version version = RunBase::getVersion(packedClass);
  switch (version)
  {
    case #CurrentVersion:
    [version, #CurrentList] = packedClass;
    break;
    default:
    return false;
  }
  return true;
}

The main method

This is the class entry point, executed when a menu item that points to it is started, or if it is opened directly from the Application Object Tree (AOT). The following code shows us the main method:

static void main(Args args)
{
  // Create an object of the ImportFile class
  ImpExpFileDialog      importFileDialog = new ImpExpFileDialog();
  // Prompt the user with the fields
  // specified in the dialog() method
  if (importFileDialog.prompt())
  // If the user hits the Ok-button
  // execute the run() method
  importFileDialog.run();
}

The ImpExpFile class

This is the main class in this class hierarchy. Any code generic to the entity classes that extend this class are set here. The class is abstract, which means that you can never create an object of this class, only its subclasses can be created (the entity classes). The following are the methods within this class:

The classDeclaration method

It is the main class for the ImpExpFile class hierarchy. The class is abstract to ensure that an object cannot be created based on it (only subclasses of this class can have objects created). The following code shows us the classDeclaration method:

abstract class ImpExpFile
{
  // Use the file macro to set IO flags
  #File
  // Global variables
  FileName            fileName;
  FileType            fileType;
  FileEntity          entity;
  IO                  file;
  ReadWrite           readWrite;
}

The new method

It is the constructor that is used to set default values for some of the global variables. The following code shows us the new method:

void new(FileType _fileType, FileName _fileName, FileEntity _entity, ReadWrite _readWrite)
{
  fileType = _fileType;
  fileName = _fileName;
   entity = _entity;
  readWrite = _readWrite;
}

The openFile method

This method is used to open the selected file either to read from or to write to. The following code shows us the openFile method:

protected boolean openFile()
{
  boolean ret = true;
  str     rw;
  if (readWrite == ReadWrite::Read)
  rw = #io_read;
  else
  rw = #io_write;
  switch (fileType)
  {
    case FileType::Binary :
    file = new BinaryIo(filename, rw);
    break;
    case FileType::Comma :
    file = new CommaIO(filename, rw);
    break;
    default :
    ret = false;
    break;
  }
  return ret;
}

The readFile method

This method is used to initialize the file that is to be read. The following code shows us the readFile method:

protected void readFile()
{
  if(!this.openFile())
  throw error("Unable to open the selected file");
  file.inFieldDelimiter(';'),
  file.inRecordDelimiter('
'),
}

The run method

This is the main execution flow of the ImpExpFile. It decides whether to read to or write from the file and catch any exceptions that might occur. The following code shows us the run method in it:

void run()
{
  try
  {
    // Use this function to make sure that
    // the mouse-pointer changes to a timeglass.
    startLengthyOperation();
    if (readWrite == ReadWrite::read)
    this.readFile();
    else
    this.writeFile();
  }
  catch (Exception::Deadlock)
  {
    retry;
  }
  catch (Exception::Error)
  {
    error(strfmt("An error occured while trying to read the file %1 into the %2 entity", filename, enum2str(entity)));
  }
  // Mous-pointer can switch back
  // to normal again.
  endLengthyOperation();
}

The writeFile method

This method is used to initialize the file that is to be written to. The following code shows us the writeFile method in it:

protected void writeFile()
{
  if(!this.openFile())
  throw error("Unable to open the selected file");
  file.outFieldDelimiter(';'),
  file.outRecordDelimiter('
'),
}

The construct method

Create an object for one of the subclasses based on the user input in the dialog. The following code shows us the construct method:

static ImpExpFile construct(FileType _fileType, FileName _filename, FileEntity _entity, ReadWrite _readWrite)
{
  ImpExpFile          impExpFile;
  switch(_entity)
  {
    case FileEntity::Cars :
    impExpFile = new ImpExp_Cars(_fileType, _filename, _entity, _readWrite);
    break;
    case FileEntity::Rentalts :
    impExpFile = new ImpExp_Rentals(_fileType, _filename, _entity, _readWrite);
    break;
  }
  return impExpFile;
}

The ImpExp_Cars class

This is one of the entity classes that I have created to demonstrate how you can use this example framework. It allows for the export and import of data from and to the CarTable respectively. Note that it only supports the insertion of new records into the CarTable, not updating. You can, of course, add that part yourself if you have read the previous chapter. The following are the methods within this class.

The classDeclaration method

It's an empty class declaration as there are no global variables in this class, and it is as follows:

class ImpExp_Cars extends ImpExpFile
{
}

The readFile method

This example shows a hardcoded way of reading the file that is to be used to insert new records into the CarTable. Each line in the file is read into a container. Each field is fetched from the container using the conpeek() function:

void readFile()
{
  CarTable        carTable;
  container       con;
  super();
  con = file.read();
  if (conlen(con) != 5)
  throw error("The file has an illegal format");
  // Read the file as long as the file is ok.
  // The status changes when the cursor hits
  // the end of the file.
  while (file.status() == IO_Status::Ok)
  {
    carTable.clear();
    carTable.CarId = conpeek(con, 1);
    carTable.CarBrand = conpeek(con, 2);
    carTable.Model = conpeek(con, 3);
    carTable.ModelYear = conpeek(con, 4);
    carTable.Mileage = conpeek(con, 5);
    if (carTable.validateWrite())
    carTable.insert();
    con = file.read();
  }
}

The writeFile method

It is used to write information from the carTable to the file, and it is implemented as shown in the following code:

void writeFile()
{
  CarTable        carTable;
  container       con;
  super();
  while select carTable
  {
    con = conins(con, 1,
    carTable.CarId,
    carTable.CarBrand,
    carTable.Model,
    carTable.ModelYear,
    carTable.Mileage);
    file.writeExp(con);
  }
}

The ImpExp_Rentals class

This entity class is written to export and import all fields from the rental table. It can also be rewritten so that it is generic and it works with all tables; but for now I just want to show you how to use the DictTable and DictField classes while exporting and importing. The following are the methods within this class.

The classDeclaration method

It is an empty class declaration as there are no global variables in this class, and is as follows:

class ImpExp_Rentals extends ImpExpFile
{
}

The readFile method

This method uses the DictTable and DictField classes to find all the fields in the RentalTable and insert the data from the file to the matching fields in the RentalTable. The following code shows us the readFile method in it:

void readFile()
{
  RentalTable     rentalTable;
  container       con;
  DictTable       dTable = new DictTable(tablenum(RentalTable));
  DictField       dField;
  int             i, fields, field;
  super();
  con = file.read();
  while (file.status() == IO_Status::Ok)
  {
    for (i=1; i <= conlen(con); i++)
    {
      field = dTable.fieldCnt2Id(i);
      dField = dTable.fieldObject(field);
      switch (dField.baseType())
      {
        case Types::Guid :
        rentalTable.(field) = str2guid(conpeek(con, i));
        break;
        case Types::Int64 :
        rentalTable.(field) = str2Int64(conpeek(con, i));
        break;
        case Types::Date :
        rentalTable.(field) = str2Date(conpeek(con, i),321);
        break;
        default :
        rentalTable.(field) = conpeek(con, i);
        break;
      }
    }
    if (rentalTable.validateWrite())
    rentalTable.insert();
    con = file.read();
  }
}

The writeFile method

This method uses the DictTable and DictField class to loop through all the fields in RentalTable and write their content to the file. The following code shows us the writeFile method:

void writeFile()
{
  RentalTable     rentalTable;
  container       con;
  int             fields, i, fieldId;
  DictTable       dt = new DictTable(tablenum(RentalTable));
  super();
  while select rentalTable
  {
    con = connull();
    fields = dt.fieldCnt();
    for (i=1; i<=fields; i++)
    {
      fieldId = dt.fieldCnt2Id(i);
      con = conins(con, i, rentalTable.(fieldId));
    }
    file.write(con);
  }
}
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.191.88.249