How to use DataFS

After installing DataFS and creating a new instance (see DataFS Setup) you need to prepare the instance by extending the schema and create some named objects that your application can use as entry point.

Create a DTDL file

A DTDL file defines the data types of your application. Those types form the 'schema' of a domain. You can share types with other applications (e.g. in common files and #include them into your project). But every application should bring its own shema extension in form of a bdtd file that contains the full set of types it needs. During the schema extension process duplicated types are detected and reused.

You can create a DTDL file in a text editor or in Visual Studio. If you use Visual Studio, create a C++ project and then add a new item. Then select 'DTDL file'. This will activate the BuildCustomization and add a file that will be compiled when the project compiles (or by right clicking the file and selecting 'compile'). If you build the file in a text editor, you have to call the DTDL compiler yourself.

Sample DTDL file:
DTDL - datatypes.dtdl
["root object"]
class TestRoot [id({1B013DB1-5219-4363-9E02-BD85D7E24413})]
{
    wstring           RootName;
    TestObjectList    AllObjects;
};

["object list"]
list TestObjectList [id({3F96FB26-623E-4C52-86AF-2CBC89F5C23A}), ck]
{
    object            anObject;
    int32             theType;
};

/*
    the idea is to store objects of type 'TestObject'
    in the 'anObject' variable of the 'TestObjectList'
    items

    remark: it is no restriction!
*/
objecttype TestObjectList.anObject(TestObject);

// this class has no description
class TestObject [id({B06E97E6-15D7-4B70-B94F-679F858E0FE8})]
{
    wstring           Text;
    int32             Number;
};

This sample DTDL file defines 2 classes and a list. The idea is to build an object of type 'TestRoot' and then create multiple objects of type 'TestObject' and add them to the 'AllObjects' list attribute.

Extending the schema

After compiling the DTDL file you receive a BDTD file. This file can be used to extend the schema of the domain. To do that you can either use DataFS_Tools (sorry, currently undocumented, that will follow soon). Or you can write a program that does it for you.

Remarks: The following sample program does almost not catch any errors. This is done to make it easier to see the main function. It's not recommended to do that in production.

C++
#include "stdafx.h"

#include <DataFS\Client\DataFS Client.h>
//using namespace DataFoundation;

int _tmain(int argc, wchar_t* argv[])
{
    const wchar_t* strServerAddress = argv[1];

    UINT16 usServerPort = (UINT16)_wtoi(argv[2]);

    GUID guidDomainId;
    ::CLSIDFromString(argv[3], &guidDomainId);

    // connect
    DataFoundation::InitializeThread();

    DataFoundation::Connection* pConnection;
    DataFoundation::Connection_Create(&pConnection);

    if(FAILED(pConnection->ConnectW(strServerAddress, usServerPort, NULL)))
    {
        DataFoundation::Connection_Destroy(pConnection);
        DataFoundation::UninitializeThread();
        return -1;
    }

    // extend schema
    BYTE* pBdtd = ...;      // content of the BDTD file -> load from file or resource or otherwise
    int iBdtdSize = ...;    // size of the buffer pBdtd is pointing to

    DataFoundation::USchemaEdit* pSchema;
        
    if(SUCCEEDED(pConnection->QuerySchemaEdit(&pSchema, &guidDomainId)))
    {
        pSchema->CreateFromBinary(pBdtd, iBdtdSize);
        pSchema->Commit();
        pSchema->Release();
    }

    pConnection->Disconnect();
    DataFoundation::Connection_Destroy(pConnection);
    DataFoundation::UninitializeThread();

    return 0;
}

Create a DADL file

A DADL file defines how you use the data types that you defined in the DTDL file. DADL files are specific for every application. Even when using the same DTDL types, you can use different DADL files and definitions. The DADL file is a way to generate access classes for your application so that you don't have to write them by hand.

In theory you don't need DADL to work with DataFS. But it makes it much easier because a lot of functions are generated.

You can create a DADL file in a text editor or in Visual Studio. If you use Visual Studio, create a C++ project and then add a new item. Then select 'DADL file'. This will activate the BuildCustomization and add a file that will be compiled when the project compiles (or by right clicking the file and selecting 'compile'). If you build the file in a text editor, you have to call the DADL compiler yourself.

Preparing the database

After compiling the DADL file you can use the classes it generated. The first step is to create all the objects that your application will need as entry points in normal operation.

Remarks: The following sample program does almost not catch any errors. This is done to make it easier to see the main function. It's not recommended to do that in production.

DADL - dataprepare.dadl
#import <datatypes.bdtd>

object ITestRoot
{
    TestRoot
    {
        RootName            [ get, set ];
    }
};

create (ITestRoot);

C++
#include "stdafx.h"

#include <DataFS\Client\DataFS Client.h>
//using namespace DataFoundation;

#include <DataFS\Access\DataFS Access.h>
using namespace DataFoundationAccess;

#include "_data\dataprepare.h"

// {56F8EB44-B7E3-4564-B9A6-22E5E1B9110C}
const GUID guidRootName = 
{ 0x56f8eb44, 0xb7e3, 0x4564, { 0xb9, 0xa6, 0x22, 0xe5, 0xe1, 0xb9, 0x11, 0xc } };

int _tmain(int argc, wchar_t* argv[])
{
    const wchar_t* strServerAddress = argv[1];

    UINT16 usServerPort = (UINT16)_wtoi(argv[2]);

    GUID guidDomainId;
    ::CLSIDFromString(argv[3], &guidDomainId);

    UINT32 ulStorageId = 0; // currently always 0

    // connect
    DataFoundation::InitializeThread();

    WDomain* pWDomain = NewWDomain(0);
    if(FAILED(pWDomain->Initialize()))
    {
        DeleteWDomain(pWDomain);
        DataFoundation::UninitializeThread();
        return -1;
    }

    if(FAILED(pWDomain->Connect(strServerAddress, usServerPort, &guidDomainId)))
    {
        pWDomain->Uninitialize();
        DeleteWDomain(pWDomain);
        DataFoundation::UninitializeThread();
        return -1;
    }

    if(FAILED(pWDomain->QueryStorage(ulStorageId, false)))
    {
        pWDomain->DisconnectAll();
        pWDomain->Uninitialize();
        DeleteWDomain(pWDomain);
        DataFoundation::UninitializeThread();
        return -1;
    }

    // bind types
    dataprepare::Bind(pWDomain);

    // create named object

    ITestRoot* pRootObject;

    dataprepare::Create(pWDomain, &pRootObject);

    pRootObject->SetRootName(L"first test root");

    pRootObject->StoreData();

    pWDomain->InsertNamedObject(&pRootObject->BuildLink(true), &guidRootName, L"first entry point");

    pRootObject->Release();

    pWDomain->Execute(TRANSACTION_STORE);

    // unbind types
    dataprepare::Unbind();

    // disconnect

    pWDomain->ReleaseStorage(ulStorageId);
    pWDomain->DisconnectAll();
    pWDomain->Uninitialize();
    DeleteWDomain(pWDomain);
    DataFoundation::UninitializeThread();

    return 0;
}

Using the database

After the database is built, you can use it by simply working with the generated W-Classes.

Remarks: The following sample program does almost not catch any errors. This is done to make it easier to see the main function. It's not recommended to do that in production.

DADL - dataaccess.dadl
#import <datatypes.bdtd>

object ITestRoot
{
    TestRoot
    {
        RootName            [ get, set ];
        AllObjects          [ get, set ];
    }

    create(ITestObject); open(ITestObject);
};

open(ITestRoot);

object ITestObject
{
    TestObject
    {
        Text                [ get, set ];
        Number              [ get, set ];
    }
};

C++
#include "stdafx.h"

#include <DataFS\Client\DataFS Client.h>
//using namespace DataFoundation;

#include <DataFS\Access\DataFS Access.h>
using namespace DataFoundationAccess;

#include "_data\dataaccess.h"

// {56F8EB44-B7E3-4564-B9A6-22E5E1B9110C}
const GUID guidRootName = 
{ 0x56f8eb44, 0xb7e3, 0x4564, { 0xb9, 0xa6, 0x22, 0xe5, 0xe1, 0xb9, 0x11, 0xc } };

int _tmain(int argc, wchar_t* argv[])
{
    const wchar_t* strServerAddress = argv[1];

    UINT16 usServerPort = (UINT16)_wtoi(argv[2]);

    GUID guidDomainId;
    ::CLSIDFromString(argv[3], &guidDomainId);

    UINT32 ulStorageId = 0; // currently always 0

    // connect
    DataFoundation::InitializeThread();

    WDomain* pWDomain = NewWDomain(0);
    if(FAILED(pWDomain->Initialize()))
    {
        DeleteWDomain(pWDomain);
        DataFoundation::UninitializeThread();
        return -1;
    }

    if(FAILED(pWDomain->Connect(strServerAddress, usServerPort, &guidDomainId)))
    {
        pWDomain->Uninitialize();
        DeleteWDomain(pWDomain);
        DataFoundation::UninitializeThread();
        return -1;
    }

    if(FAILED(pWDomain->QueryStorage(ulStorageId, false)))
    {
        pWDomain->DisconnectAll();
        pWDomain->Uninitialize();
        DeleteWDomain(pWDomain);
        DataFoundation::UninitializeThread();
        return -1;
    }

    // bind types
    dataaccess::Bind(pWDomain);

    // open named object

    DataFoundation::ObjectId oiRootObject;

    pWDomain->QueryNamedObject(&oiRootObject, &guidRootName);

    ITestRoot* pRootObject;
    dataaccess::Open(pWDomain, &pRootObject, &oiRootObject);

    pRootObject->Load();
    pWDomain->Execute(TRANSACTION_LOAD);

    // open the list for writing

    TestObjectList* pList;
    pRootObject->SetAllObjects(&pList);

    // create an add a new object

    ITestObject* pTestObject;

    pRootObject->Create(&pTestObject);

    TestObjectListItem itm;
    itm.anObject = pTestObject->BuildLink(true);
    itm.theType = 12;

    pList->Insert(NULL, &itm);

    pTestObject->SetText(L"something");
    pTestObject->SetNumber(343);

    pTestObject->StoreData();
    pRootObject->StoreData();

    pWDomain->Execute(TRANSACTION_STORE);

    pTestObject->Release();
    pRootObject->Release();

    // unbind types
    dataaccess::Unbind();

    // disconnect

    pWDomain->ReleaseStorage(ulStorageId);
    pWDomain->DisconnectAll();
    pWDomain->Uninitialize();
    DeleteWDomain(pWDomain);
    DataFoundation::UninitializeThread();

    return 0;
}
© 2020 Mobiland AG