D365FO. Working with Azure File storage.

Azure Storage - Files

Current version of AX uses Azure Blob storage for various things like document handling, retail CDX files, DIXF and Excel add-in. You can find several blogs explaining how to upload and download files to Blob, SharePoint or temporary storage. However, what about file shares?

Azure File storage implements SMB 3.0 protocol and could be easily mapped to your local computer. You need just a few minutes to create new storage account and mount it, watch this how-to video for details.

To read file from newly created share we can use next code:

using Microsoft.Azure;
using Microsoft.WindowsAzure.Storage;
using Microsoft.WindowsAzure.Storage.Blob;
using Microsoft.WindowsAzure.Storage.File;

class RunnableClass1
{
    public static void main(Args _args)
    {
        System.IO.MemoryStream memoryStream;

        var storageCredentials = new Microsoft.WindowsAzure.Storage.Auth.StorageCredentials('AzureStorageAccountName', 'AzureStorageAccountKey');

        CloudStorageAccount storageAccount = new Microsoft.WindowsAzure.Storage.CloudStorageAccount(storageCredentials, true);

        CloudFileClient fileClient = storageAccount.CreateCloudFileClient();

        CloudFileShare share = fileClient.GetShareReference('AzureStorageShareName');

        if (share.Exists(null, null))
        {
            CloudFileDirectory rootDir = share.GetRootDirectoryReference();

            CloudFileDirectory fileDir = rootDir.GetDirectoryReference('folder');

            if (fileDir.Exists(null, null))
            {
                CloudFile file = fileDir.GetFileReference('file.txt');

                if (file.Exists(null, null))
                {
                    memoryStream = new System.IO.MemoryStream();
                    file.DownloadToStream(memoryStream, null, null, null);
                }
            }
        }
    }
}

References:

Edited:

Azure File Storage client source code could be found on GitHub

Advertisements

D365O. Trick to pass a value between Pre and Post event handler using XppPrePostArgs.

Recently we came across a scenario where we needed to check if a field has changed after super() in update method of a table. Back in the days of AX 2012 you could easily compare original field’s value with current using orig() method before super() and call necessary logic after.

public void update()
{
    boolean myFieldHasChanged = this.MyField != this.orig().MyField;

    super();

    if (myFieldHasChanged)
    {
        this.doStuff();
    }
}

Now we want to do the same using extensions. We can create Pre and Post event handlers, but they are static, so we need a way to pass a value between them.
First option is to use static field, like it’s done in RunBase extension example

public static class MyTableEventHandler
{
    private static UnknownNoYes myFieldHasChanged;

    [PreHandlerFor(tableStr(MyTable), tableMethodStr(MyTable, update))]
    public static void MyTable_Pre_update(XppPrePostArgs _args)
    {
        MyTable myTable = _args.getThis() as MyTable;

        if (myTable.MyField != myTable.orig().MyField)
        {
            MyTableEventHandler::myFieldHasChanged = UnknownNoYes::Yes;
        }
        else
        {
            MyTableEventHandler::myFieldHasChanged = UnknownNoYes::No;
        }
    }

    [PostHandlerFor(tableStr(MyTable), tableMethodStr(MyTable, update))]
    public static void MyTable_Post_update(XppPrePostArgs _args)
    {
        MyTable myTable = _args.getThis() as MyTable;

        if (MyTableEventHandler::myFieldHasChanged == UnknownNoYes::Yes)
        {
            myTable.doStuff();
        }

        MyTableEventHandler::myFieldHasChanged = UnknownNoYes::Unknown;
    }
}

Another option is to use XppPrePostArgs as a vehicle for new parameter. XppPrePostArgs has collection of parameters under the hood, so nothing stops us to add one more and framework will take care of passing it between Pre and Post event handler!

XppPrePostArgs_collection.jpg

public static class MyTableEventHandler_XppPrePostArgs
{
    const static str myFieldHasChangedArgName = 'myFieldHasChanged';

    [PreHandlerFor(tableStr(MyTable), tableMethodStr(MyTable, update))]
    public static void MyTable_Pre_update(XppPrePostArgs _args)
    {
        MyTable myTable = _args.getThis() as MyTable;

        boolean myFieldHasChanged = myTable.MyField != myTable.orig().MyField;

        <strong>_args.addArg(MyTableEventHandler_XppPrePostArgs::myFieldHasChangedArgName, myFieldHasChanged);</strong>
    }

    [PostHandlerFor(tableStr(MyTable), tableMethodStr(MyTable, update))]
    public static void MyTable_Post_update(XppPrePostArgs _args)
    {
        MyTable myTable = _args.getThis() as MyTable;

        <strong>boolean myFieldHasChanged = _args.getArg(MyTableEventHandler_XppPrePostArgs::myFieldHasChangedArgName);
</strong>
        if (myFieldHasChanged)
        {
            myTable.doStuff();
        }
    }
}

Using one of these approaches you should remember that static fields apply to the class, not to instances of the class, so do not mix well with concurrency. Trick with XppPrePostArgs tightly depends on current implementation, that could be changed anytime and comes with no warranty.

To overcome this and other limitations of extensions capabilities Microsoft is introducing Method wrapping and chain of command and I’m pretty sure that we’ll see blogs on this from my MVP fellows soon.