Tuesday, January 21, 2020

Date effective child form in Dynamics 2012

in a form to display the date effective filter pane you have to add this snippet in the form init method:

DateEffectivenessPaneController::constructWithForm(
        this,
        MYD_ClinicianPositions_ds);

the DateEffectivenessPaneController is responsible of creating all the user interface and the interaction.

image

if you also have a child form which is also date effective and you want it also to be filtered based on the date effective selection on the parent form you can do this:

public class FormRun extends ObjectRun
{
    SysFormSplitter_X verticalSplitter;
    DateEffectivenessPaneController effectivenessPaneController;
}

public void init()
{
    super();
    //Initialize splitter
    verticalSplitter = new SysFormSplitter_X(VSplitter, GridContainer, element, 300);

    //initialize the DateEffectivenessPaneController
    effectivenessPaneController = DateEffectivenessPaneController::constructWithForm(this, MYD_Mentor_ds);
}

then override the executeQuery method of the subform darasource:

public void executeQuery()
{
    FromDate fromDate = dateNull();
    ToDate toDate = dateMax();
    
    TransDate showAsOfDate;
    FormCheckBoxControl showAllCheckbox;
    boolean showAll;
    
    //get the selection from the parent form date effective toolbar
    showAsOfDate = effectivenessPaneController.parmShowAsOfDate();
    showAllCheckbox =  effectivenessPaneController.parmShowAllCheckbox();
    showAll = showAllCheckbox.value();
    
    if (showAll)
        this.query().validTimeStateDateRange(fromDate, toDate);
        
    if (showAsOfDate)
        this.query().validTimeStateAsOfDate(showAsOfDate);

    super();
}

Monday, December 30, 2019

Index consideration for date effective tables

it is a best practice to add ValidTo to the ValidTimeStateKey index
and on the Properties tab (of the ValidTo field in the index) to
select Yes from the Included Column.

image

[see: http://dev.goshoom.net/en/2012/04/included-columns-in-ax2012]

but if it is a clustered index see this:

Performance considerations when designing valid time state tables
To help improve performance of valid time state tables, you should index them correctly.
Valid time state tables are modeled with an alternate key that includes the ValidFrom column.
In some models, the ValidTo column may have also be included in the alternate key,
but this is not necessary for uniqueness, and it should be removed from the alternate key constraint.
If the ValidFrom column is a key column of the clustered index,
the ValidTo column should not also be a key column of the clustered index
.
If the ValidFrom column is a key column of a non-clustered index,
the ValidTo column should be made an included column in the non-clustered index,
which provides coverage for range queries that involve both ValidTo and ValidFrom columns.

for examples see where the index is a clustered index see:
\Data Dictionary\Tables\LogisticsPostalAddress

image

or see
\Data Dictionary\Tables\HcmPositionWorkerAssignment

image

Friday, December 6, 2019

SysExtension framework for Dynamics Ax example

The benefits of using this new extension model are that the base and derived classes are decoupled, and it takes less code to extend the capability of the Microsoft Dynamics AX application.

The getClassFromSysAttribute method works by searching through the classes that are derived from the our base class (EF_ExtFrameworkSample6) until it finds a class that has matching attribute

The input value of the attribute class can be anything, an enum, a string, an integer. For this example I used the class name as input.

1) Create the attribute class

As you see I added in the attribute class also a generic static method that can be used by any extension that might use this attribute

class EF_ClassNameAttribute extends SysAttribute
{
    ClassName className;
}
public void new(ClassName _className)
{
    super();
    className = _className;
}
public ClassName parmClassName(ClassName _className = className)
{
    className = _className;

    return className;
}
//this method should be in the factory class, not here.
//I took the liberty to put it here becuse I might want to share it with multiple factory classes.
public static Object getClassFromSysAttribute(ClassName _baseClassName, ClassName _className)
{
    EF_ClassNameAttribute attr;
    Object cl;

    attr = new EF_ClassNameAttribute(_className);
    cl = SysExtensionAppClassFactory::getClassFromSysAttribute(_baseClassName, attr);

    if (!cl)
    {
        throw error(Error::wrongUseOfFunction(_baseClassName));
    }

    return cl;
}



2) Create the base class

the base class does not need to be abstract, but is a good practice

abstract class EF_ExtFrameworkSample6
{
    Name name;
    MethodName functionName;
}
abstract protected void init()
{
}
public void run()
{
    this.init();
    info(strFmt('Hello my name is %1', name));
    info(strFmt('the class that run is %1', functionName));
}


3) Create the Extensions


[EF_ClassNameAttribute(classStr(EF_ExtFrameworkSample6_1))]
class EF_ExtFrameworkSample6_1 extends EF_ExtFrameworkSample6
{
}
protected void init()
{
    name = 'Pippo';
    functionName = funcName();
}
[EF_ClassNameAttribute(classStr(EF_ExtFrameworkSample6_2))]
class EF_ExtFrameworkSample6_2 extends EF_ExtFrameworkSample6
{
}
protected void init()
{
    name = 'Topolino';
    functionName = funcName();
}


4) Create the Factory class


class EF_ExtFrameworkSample6Factory
{
}
protected void new()
{
}
public static EF_ExtFrameworkSample6 newFromClassName(ClassName _className)
{
    return EF_ClassNameAttribute::getClassFromSysAttribute(classStr(EF_ExtFrameworkSample6), _className);
}
public static EF_ExtFrameworkSample6 newFromSample6_1()
{
    return EF_ExtFrameworkSample6Factory::newFromClassName(classStr(EF_ExtFrameworkSample6_1));
}
public static EF_ExtFrameworkSample6 newFromSample6_2()
{
    return EF_ExtFrameworkSample6Factory::newFromClassName(classStr(EF_ExtFrameworkSample6_2));
}
//just for trials...
public static void main(Args _args)
{
    EF_ExtFrameworkSample6 cl;
    cl = EF_ExtFrameworkSample6Factory::newFromSample6_1();
    cl.run();
}

image

One side note, The extension framework cache so every time you do some changes during development clear the cache :

static void JobEF_SysExtensionCache(Args _args)
{
    SysExtensionCache::clearAllScopes();
}

Tuesday, September 24, 2019

Maintain Fast SQL Operations avoid set based operation to fall back to row by row operation using skip methods in Dynamics ax

Sometimes when importing large number of records (Eg. millions of lines), the quickest way is to use:

  • insert_recordset
  • update_recordset
  • delete_from

But these set base operation will roll back to row-by-row operation when either of the following condition is true:
  • it is not an SQL table (Eg. temporary table)
  • Database log is enabled for the table
  • Alert is setup for this table
  • Record level security is enabled for the table
  • AOSValidation method is overwritten
  • when using insert_recordset
    • the .insert() method is overwritten
  • when using update_recordset
    • the .update() method is overwritten
  • when using delete_from
    • the .delete() method is overwritten
    • DeleteAction is defined

To counter this, you can call a number of skip* methods:

  • common.skipDataMethods(true) will skip the insert/update/delete methods
  • common.skipDeleteMethod(true) will skip the delete method
  • common.skipDeleteActions(true) will not execute the delete actions
  • common.skipEvents(true) if alert is setup

image

see: https://docs.microsoft.com/en-us/dynamicsax-2012/developer/maintain-fast-sql-operations

I saw an example in this post: https://dynamicsuser.net/ax/f/developers/76285/update_recordset

public server static void MyCustomMethod(SalesLine _salesLine)
{
    salesLine           salesLineUpd;
    InventDim           inventDim;
    
  if ( _salesLine.MyField )
  {
    inventDim = _salesLine.inventDim();

    salesLineUpd.skipDataMethods( true );

    ttsBegin;

    update_recordSet salesLineUpd
    setting PER_ChairSerialNo = inventDim.inventSerialId
    where salesLineUpd.SalesId == _salesLine.SalesId
    && salesLineUpd.RecId != _salesLine.RecId; // update every other line with serial id

    ttsCommit;
  }


I also saw in this other post some suggestions regarding database log
http://www.artofcreation.be/2014/08/11/what-you-should-know-about-database-logging-functional-impact/

If you really want to activate database logging but you have code that need to do a set-based operation, you can get around this issue by using the skipDatabaseLog method in combination with the other skip* methods.

However, in my opinion it is better not to use database log in the first place. So these are my recommendations about database logging:

  • Do not use it.
  • If you do use it, make sure it is for a good reason and document why.
  • Do not use database logging because you do not trust your employees or as a form of “security”.
  • When activating the database log for a table, pay close attention to the TableGroup property of the table. It is fairly safe to activate the database log on tables with table group Main, Group and Parameter. Activating it for other groups such as Transaction, TransactionHeader, TransactionLine, WorksheetHeader and WorksheetLine is usually bad.
  • If you are a consultant, capture the need for database logging in the analysis phaseand set this up in your DEV/TST/ACC/… environments as early as possible.
  • Do not simply activate database logging in a production environment and expect everything to go well, test it first in an other environment as if it were a code change

Monday, September 9, 2019

Simulated Multiple Inheritance fox x++ in Dynamics Ax

We all know that in X++, a class can only extend one class; multiple inheritance is not supported in X++ and to overcome that we can use Interfaces. What I never saw is an example of multiple inheritance in X++. Instead I saw someone saying it’s a bug! I don’t know if was serious or a was making a joke…

Let’s start making things clear:

In Multiple inheritance, one class can have more than one superclass and inherit features from all its parent classes. As shown in the below diagram, class C inherits the features of class A and B.

Most recent languages like C# support only simple inheritance. They don't have multiple inheritance because their designers had to choose between have it in and have all the problems it comes with, or get it out of the language putting away all those problems, and introduce a versatile and less problematic substitute like interfaces and interface inheritance.
Multiple inheritance has a pathological problem:

Imagine, we want to have a class Child which inherits from Parent classes ParentA and ParentB. Both classes have the same methods MethodA and MethodB.
Now, when we instantiate the Class Child then calling MethodA will confuse the compiler that does not know from which class MethodA should be called.

The traditional way to emulate multiple inheritance with interface inheritance.

Let us see how to simulate multiple Inheritance in X++

image

first we create the interfaces

interface EF_IMultipleInheritaceBaseClass1
{
}
public void x()
{
}
public void y()
{
}

interface EF_IMultipleInheritaceBaseClass2
{
}
public void z()
{
}

next we can create two classes that implement those interfaces

class EF_MultipleInheritaceBaseClass1 implements EF_IMultipleInheritaceBaseClass1
{
}
public void x()
{
    info(funcName());//it shows the class and method name
}
public void y()
{
    info(funcName());
}

class EF_MultipleInheritaceBaseClass2 implements EF_IMultipleInheritaceBaseClass2
{
}
public void z()
{
    info(funcName());
}

now we are ready to create our abstract class that implements the two interfaces

abstract class EF_MultipleInheritaceBaseClass1And2 implements EF_IMultipleInheritaceBaseClass1, EF_IMultipleInheritaceBaseClass2
{
    EF_IMultipleInheritaceBaseClass1 class1;
    EF_IMultipleInheritaceBaseClass2 class2;
}
protected void new()
{
    class1 = classFactory.createClass(classNum(EF_MultipleInheritaceBaseClass1));
    class2 = classFactory.createClass(classNum(EF_MultipleInheritaceBaseClass2));
}
public void x()
{
    class1.x();
}
public void y()
{
    class1.y();
}
public void z()
{
    class2.z();
}

finally we can extend our abstract class and bingo, we simulated multiple inheritance:

class EF_MultipleInheritaceClass1And2 extends EF_MultipleInheritaceBaseClass1And2
{
}
public static EF_IMultipleInheritaceClass1And2 construct()
{
    return new EF_IMultipleInheritaceClass1And2();
}
public static void main(Args _args)
{
    EF_IMultipleInheritaceClass1And2 class1And2;
    class1And2 = EF_IMultipleInheritaceClass1And2::construct();
    class1And2.x();
    class1And2.y();
    class1And2.z();
}

Convert a Dynamics Enum to a C# generic collection

When you need to consume a Dynamics Ax Enum type in a .Net project you should make your project independent from the internal structure of Dynamics and certainly you do not want to create a static copy of your Dynamics Enum in your .Net project.

In order to achieve that I taught to expose the Dynamics Enum as a generic List.

To do that and adhere to the DRY principle (do not repeat yourself) I made an extension method in C# that converts any Enum in a generic list.

first you need to create a POCO class, a data transfer object

namespace MydDynamicsIntegration.Models
{
    public class AxEnumDefinition
    {
        public int Value { get; set; }
        public string Name { get; set; }
        public string Label { get; set; }
    }
}

now here the extension method

using System;
using System.Collections.Generic;
using MydDynamicsIntegration.Models;

namespace MydDynamicsIntegration.DynamicsCommon
{
    public static class AxGetValuesExtensions
    {
        public static List<AxEnumDefinition> FromAxToColl<T>(this T eEnum) where T : struct, IConvertible
        {
            var enumDefinitions = new List<AxEnumDefinition>();

            Type t = typeof(T);

            if (!t.IsEnum)
                throw new ArgumentException("T must be an enumerated type");

            try
            {
                SysDictEnum sysDictEnum = SysDictEnum.newName(t.Name);

                for (int i = 0; i < sysDictEnum.values(); i++)
                {
                    enumDefinitions.Add(new AxEnumDefinition()
                    {
                        Value = sysDictEnum.index2Value(i),
                        Name = sysDictEnum.index2Name(i),
                        Label = sysDictEnum.index2Label(i)
                    });
                }
            }
            catch
            {
                //
            }

            return enumDefinitions;
        }
    }
}

now you can use it like this

use example: 
return new HcmDiscussionStatus().FromAxToColl();
OR to a specific value
Status = new AxEnumDefinition()
   {
      Value = Global.enum2int(discussion.status),
      Name = Global.enum2Value(discussion.status),
      Label = discussion.status.FromAxToColl().Where(x => x.Value == Global.enum2int(discussion.status)).Select(x => x.Label).FirstOrDefault()
   }
OR 
Status = discussion.status.FromAxToColl().FirstOrDefault(x => x.Value == Global.enum2int(discussion.status))

Thursday, September 5, 2019

Attach documents in Dynamics Ax programmatically in x++ code. different options

I had to do some custom development to upload/Attach document in Ax programmatically from a webApi. So I did some research on what is the best way to it.

here I share the different options I found to do it.

OPTION 1
USE \Classes\DocuActionArchive\add (it extends DocuActionFile)

NOTE, is the preferred way, but doesn't let you customize anything. the description of the file is the file name

static void JobEF_DocumentInsert(Args _args)
{
    DocuType docuType;
    DocuTypeId docuTypeId;
    DocuRef docuRef;
    DocuValue docuValue;
    DocuActionArchive archive;

    Filename filename;

    HcmDiscussion hcmDiscussion;

    docuTypeId = 'AppraisalEnrico';
    hcmDiscussion = HcmDiscussion::findByDiscussionWorker('xx', HcmWorker::findByPersonnelNumber('xx').RecId);

    filename = @'\\...\Myfile.PNG';

    if ( ! hcmDiscussion ) return;

    docuType = DocuType::find(docuTypeId);

    ttsBegin;

    docuRef.RefCompanyId = hcmDiscussion.DataAreaId;
    docuRef.RefTableId   = hcmDiscussion.TableId;
    docuRef.RefRecId     = hcmDiscussion.RecId;
    docuRef.TypeId       = docuType.TypeId;
    docuRef.insert();

    archive = new DocuActionArchive();

    archive.add(docuRef, filename);

    ttsCommit;
}

OPTION 2
USE \Classes\DocuActionFile\insertDocuValue

Note: seems quite good option. you can't use this method directly as it is an abstract class, use \Classes\DocuActionArchive\

static void JobEF_DocumentInsert2(Args _args)
{
    DocuType docuType;
    DocuTypeId docuTypeId;
    DocuRef docuRef;
    DocuValue docuValue;
    DocuActionArchive archive;

    Filename filename;

    HcmDiscussion hcmDiscussion;

    docuTypeId = 'AppraisalEnrico';
    hcmDiscussion = HcmDiscussion::findByDiscussionWorker('xx', HcmWorker::findByPersonnelNumber('xx').RecId);

    filename = @'\\...\Myfile.PNG';

    if ( ! hcmDiscussion ) return;

    docuType = DocuType::find(docuTypeId);

    ttsBegin;

    docuRef.RefCompanyId = hcmDiscussion.DataAreaId;
    docuRef.RefTableId   = hcmDiscussion.TableId;
    docuRef.RefRecId     = hcmDiscussion.RecId;
    docuRef.TypeId       = docuType.TypeId;
    docuRef.Name         = strFmt('prova - %1', Docu::getFileName(filename));
    docuRef.insert();

    archive = new DocuActionArchive();

    archive.insertDocuValue(docuRef, filename);

    ttsCommit;
}

OPTION 3
NOTE: is using Docu::insertFile. more complex.

static void JobEF_DocumentInsert1(Args _args)
{
    DocuType docuType;
    DocuTypeId docuTypeId;
    DocuRef docuRef;
    DocuValue docuValue;
    DocuActionArchive archive;

    Filename filename;
    DocuValueFile file;
    BinData binData;
    boolean fileLocked;

    HcmDiscussion hcmDiscussion;
    #File
    
    //done like: \Classes\TrvImportReceiptsBatch\processFolder
    DocuValueFile getDocuValueFile(FileName _fullPathOfFile)
    {
        binData = new BinData();

        new FileIOPermission(_fullPathOfFile,'r').assert();
        // BP Deviation documented
        binData.loadFile(_fullPathOfFile);
        CodeAccessPermission::revertAssert();
        return binData.getData();
    }
    
    //done like: \Data Dictionary\Tables\DocuValue\Methods\writeDocuValue
    DocuValueFile getDocuValueFile1(FileName _fullPathOfFile)
    {
        binData = new BinData();
        if (isRunningOnServer())
        {
            // Assert permission and get the temp filename
            new FileIOPermission(_fullPathOfFile,#io_read).assert();
            // BP deviation documented
            fileLocked = WinApiServer::fileLocked(_fullPathOfFile);
            CodeAccessPermission::revertAssert();
        }
        else
        {
            // BP deviation documented
            fileLocked = WinApi::fileLocked(_fullPathOfFile);
        }

        // Insert to database
         if (fileLocked)
         {
            info("@SYS72783");
         }
        else
        {
            // LoadFile demands read permission on the file
            new FileIOPermission(_fullPathOfFile, #io_read).assert();
            // BP deviation documented
            if (binData.loadFile(_fullPathOfFile)) //only works if file not locked
            {
                file = binData.getData();
            }
            CodeAccessPermission::revertAssert();
        }
        return file;
    }
    
    docuTypeId = 'AppraisalEnrico';
    hcmDiscussion = HcmDiscussion::findByDiscussionWorker('123', HcmWorker::findByPersonnelNumber('123').RecId);

    filename = @'\\...\Myfile.PNG';

    file = getDocuValueFile(filename);

    if ( ! hcmDiscussion ) return;

    docuType = DocuType::find(docuTypeId);

    ttsBegin;

    docuRef.RefCompanyId = hcmDiscussion.DataAreaId;
    docuRef.RefTableId   = hcmDiscussion.TableId;
    docuRef.RefRecId     = hcmDiscussion.RecId;
    docuRef.TypeId       = docuType.TypeId;

    docuValue = Docu::insertFile(docuRef, filename, file, true);
    docuRef.ValueRecId = docuValue.RecId;
    docuRef.Name = 'mia prova';

    docuRef.insert();

    ttsCommit;
}

OPTION 4
USE \Classes\DocumentFileHelper\attachDocumentAsUser

SEE EXAMPLE: \Data Dictionary\Tables\RetailDiscountCode\Methods\createBarCodeImage

private void createBarCodeImage()
{
    #define.AttachmentName('BarcodeImage.jpg')

    RetailSharedParameters sharedParams;
    BarcodeSetup barcodeSetup;
    str base64ImageString;
    container attachDocumentParams;
    DocuRef DocuRefTable;
    RecId docuRefRecId;

    if (this.BarCode)
    {
        // Get selected barcode
        select firstOnly BarcodeSetupId from sharedParams
        join RetailBarcodeMask, fontName, fontSize from barcodeSetup
        where sharedParams.barcodeSetupId == barcodeSetup.barcodeSetupId;

        // Get base64 string of barcode image.
        base64ImageString = RetailBarcodeManagement::getBarcodeJpegImageAsBase64String(this.BarCode, barcodeSetup.fontName, barcodeSetup.fontSize);

        // Attach barcode image to the discount code.
        attachDocumentParams = [0, base64ImageString, #AttachmentName, '', DateTimeUtil::utcNow(), this.TableId, this.RecId, curext(), DocuType::typeFile()];
        [docuRefRecId] = DocumentFileHelper::attachDocumentAsUser(attachDocumentParams);

        // Make attached image external.
        select forUpdate DocuRefTable where DocuRefTable.RecId == docuRefRecId;
        // set document description to 'Bar code'
        DocuRefTable.Name = "@RET3053";
        DocuRefTable.Restriction = DocuRestriction::External;
        DocuRefTable.update();
    }
}

OR see \Classes\TrvUnreconciledExpenseService\createAttachments

OR \Classes\TrvReceiptService\createReceiptHelper

private DocuRef createReceiptHelper(RefTableId _tableId, RefRecId _recId, DataAreaId _dataAreaId, str _name, str _documentName, str _documentContents)
{
    Filename documentName;
    FilePath documentUrl;
    RefRecId contentType;
    str documentFile;
    DocumentFileReceiveDate receiveDate;
    DocuTypeId docuTypeId;
    TrvReceiptsHelper trvReceiptsHelper = new TrvReceiptsHelper();
    container args;
    RecId createdRecId;
    DocuRef docuRef;

    [documentName, documentUrl, contentType, receiveDate, documentFile] = DocumentFileHelper::getValidateUnpackedDocumentFileData(
                                                                _documentName, "", "",
                                                                DateTimeUtil::utcNow(), _documentContents);

    docuTypeId = trvReceiptsHelper.getDocuTypeId();

    args = [contentType, documentFile, documentName, documentUrl, receiveDate, _tableId, _recId, _dataAreaId, docuTypeId];

    [createdRecId] = DocumentFileHelper::attachDocumentAsUser(args);

    if (createdRecId != 0)
    {
        docuRef = DocuRef::findRecId(createdRecId, true);

        if (_name != '')
        {
            docuRef.Name = _name;
        }
        else
        {
            docuRef.Name = "@SYS138348";
        }
        docuRef.update();
    }

    return docuRef;
}

NOTE to get the documentContents see: \Classes\TrvReceiptService\getDocumentContents

.... 
    // Get the receipt contents and serialize them.
    binData = DOCommonDocuUtils::GetDocuContent(docuRef);
    if (binData && conLen(binData.getData()) > 0)
    {
        documentContents = binData.base64Encode();
    }

    return documentContents;


------------------------------------------------

Finally I didn’t use none of them but I ended extending an existing class which gives me the advantage to code less (always use existing methods!) and also to upload all the files in a directory in one go. So from the WebApi I create a shared folder where I upload all the documents I want to be attached to an Ax record and my extended class is responsible to Attach the document.
When the process ends Ax deletes the shared folder.

class MYD_HcmDiscussionAttachmentHelper extends TrvImportReceiptsBatch
{
    HcmDiscussion hcmDiscussion;
    DocuTypeId docuTypeId;
}

//for trials only
public static void main(Args _args)
{
    FilePath dirPath;
    HcmDiscussion hcmDiscussion;

    MYD_HcmDiscussionAttachmentHelper cl = new MYD_HcmDiscussionAttachmentHelper();

    dirPath = @'\\...\temp\000198';
    hcmDiscussion = HcmDiscussion::findByDiscussionWorker('000198', HcmWorker::findByPersonnelNumber('123').RecId);

    cl.parmDirPath(dirPath);
    cl.parmHcmDiscussion(hcmDiscussion);
    cl.parmDocuTypeId('AppraisalEnrico');
    cl.run();
}

boolean importFile(Filename _filename, DocuValueFile _file)
{
    DocuRef docuRef;
    DocuValue docuValue;
    boolean ret = false;

    if (hcmDiscussion)
    {
        ttsbegin;
            docuRef.RefCompanyId = hcmDiscussion.DataAreaId;
            docuRef.RefTableId   = hcmDiscussion.TableId;
            docuRef.RefRecId     = hcmDiscussion.RecId;
            docuRef.TypeId       = docuTypeId;

            docuValue = Docu::insertFile(docuRef, _filename, _file, true);
            docuRef.ValueRecId = docuValue.RecId;
            docuRef.Name = System.IO.Path::GetFileNameWithoutExtension(_filename);

            docuRef.insert();
        ttscommit;
        ret = true;
    }

    return ret;
}

public DocuTypeId parmDocuTypeId(DocuTypeId _docuTypeId = docuTypeId)
{
    docuTypeId = _docuTypeId;

    return docuTypeId;
}

public HcmDiscussion parmHcmDiscussion(HcmDiscussion _hcmDiscussion = hcmDiscussion)
{
    hcmDiscussion = _hcmDiscussion;

    return hcmDiscussion;
}

public void run()
{
    boolean result = true;
    try
    {
        super();
    }
    catch
    {
        result = false;
    }

    if (result)
    {
        new FileIOPermission(dirPath,'W').assert();
        System.IO.Directory::Delete(dirPath, true);
        CodeAccessPermission::revertAssert();
    }
}