GitHub    Download    Forum
Overview
How to start
Download
Concepts
Tutorial
C++ API
C# API
DTDL
DADL
Setup
Download projects from GitHub

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 an 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 in your project). But every application should bring its own schema extension in the form of a BDTD file that contains the full set of types it needs. Duplicated types are detected and reused during the schema extension process.
You can create a DTDL file in a text editor or in Visual Studio. If you use Visual Studio, create a C++ or C# project and then add a new item. Then select 'DTDL file'. This will add a file that will be compiled when the project compiles (or by right-clicking the file and selecting 'compile' in C++ projects). If you build the file in a text editor, you can also call the DTDL compiler yourself.
Sample DTDL file:
DTDL – DataDefinition.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, which can be used to extend the schema of the domain. To do that, you can either use DataFS_Tools or you can write a program that does it for you.
Remarks: The following sample program catches next to no errors. This is done to make it easier to see the main function. It is not advisable to work this way in production.
Example – SchemaPrep.cpp
Example – SchemaPrep.cs
C++
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

    HRSRC hBdtd = ::FindResource(::GetModuleHandle(NULL), MAKEINTRESOURCE(IDR_RT_BDTD1), L"RT_BDTD");
    void* pBdtd = ::LockResource(::LoadResource(::GetModuleHandle(NULL), hBdtd));
    int iBdtdSize = ::SizeofResource(::GetModuleHandle(NULL), hBdtd);

    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;
}
class SchemaPrep
{
    static void Main(string[] args)
    {
        String strServerAddress = args[0];
        UInt16 usServerPort = UInt16.Parse(args[1]);
        Guid guidDomainId = Guid.Parse(args[2]);

        // connect

        DataFoundation.ThreadInit.InitializeThread();

        DataFoundation.Connection pConnection = DataFoundation.Connection.Create();

        if(0 > (pConnection.Connect(strServerAddress, usServerPort, null)))
        {
            DataFoundation.Connection.Destroy(pConnection);
            DataFoundation.ThreadInit.UninitializeThread();
            return;
        }

        // extend schema

        Stream sBdtd = System.Reflection.Assembly.GetExecutingAssembly().GetManifestResourceStream("DataDefinition");

        Byte[] pBdtd = new Byte[sBdtd.Length];
        sBdtd.Read(pBdtd, 0, (int)sBdtd.Length);

        DataFoundation.USchemaEdit pSchema;

        if(0 <= (pConnection.QuerySchemaEdit(out pSchema, guidDomainId)))
        {
            pSchema.CreateFromBinary(pBdtd);
            pSchema.Commit();
            pSchema.Release();
        }

        pConnection.Disconnect();
        DataFoundation.Connection.Destroy(pConnection);
        DataFoundation.ThreadInit.UninitializeThread();
    }
}

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 to every application. Even when using the same DTDL types, you can use different DADL files and definitions. The DADL file is a way of generating 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++ or C# project and then add a new item. Then select 'DADL file'. This will add a file that will be compiled when the project compiles (or by right-clicking the file and selecting 'compile' in C++ projects). If you build the file in a text editor, you can also call the DADL compiler yourself.

Preparing the database

After compiling the DADL file, you can use the classes it generated. The first step involves creating all the objects that your application will need as entry points in normal operation.
Remarks: The following sample program catches next to no errors. This is done to make it easier to see the main function. It is not advisable to work this way in production.
DADL – PrepareDefinition.dadl
#import <DataDefinition.bdtd>

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

Example – StoragePrep.cpp
Example – StoragePrep.cs
C++
C#
#include "stdafx.h"

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

#include "_data\PrepareDefinition.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;

    // connect

    InitializeThread();

    WDomain* pWDomain = Domain_Create();
    if(FAILED(pWDomain->Initialize(&guidDomainId)))
    {
        Domain_Destroy(pWDomain);
        UninitializeThread();
        return -1;
    }

    if(FAILED(pWDomain->Connect(strServerAddress, usServerPort, NULL)))
    {
        pWDomain->Uninitialize();
        Domain_Destroy(pWDomain);
        UninitializeThread();
        return -1;
    }

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

    // bind types

    PrepareDefinition::Bind(pWDomain);

    // create named object

    ITestRoot* pRootObject;
	ITestRoot::Create(&pRootObject, pWDomain);

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

    pRootObject->StoreData();

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

    pWDomain->Execute(Transaction::Store);

    pRootObject->Release();

    // unbind types

    PrepareDefinition::Unbind();

    // disconnect

    pWDomain->ReleaseStorage(ulStorageId);
    pWDomain->DisconnectAll();
    pWDomain->Uninitialize();
    Domain_Destroy(pWDomain);
    UninitializeThread();

    return 0;
}
using DataFoundationAccess;

class StoragePrep
{
    static readonly Guid guidRootName = new Guid("{56F8EB44-B7E3-4564-B9A6-22E5E1B9110C}");

    static void Main(string[] args)
    {
        String strServerAddress = args[0];
        UInt16 usServerPort = UInt16.Parse(args[1]);
        Guid guidDomainId = Guid.Parse(args[2]);

        UInt32 ulStorageId = 0;

        // connect

        ThreadInit.InitializeThread();

        WDomain pWDomain = WDomain.Create();
        if(0 > (pWDomain.Initialize(guidDomainId)))
        {
            WDomain.Destroy(pWDomain);
            ThreadInit.UninitializeThread();
            return;
        }

        if(0 > (pWDomain.Connect(strServerAddress, usServerPort, null)))
        {
            pWDomain.Uninitialize();
            WDomain.Destroy(pWDomain);
            ThreadInit.UninitializeThread();
            return;
        }

        if(0 > (pWDomain.QueryStorage(ulStorageId, false, null)))
        {
            pWDomain.DisconnectAll();
            pWDomain.Uninitialize();
            WDomain.Destroy(pWDomain);
            ThreadInit.UninitializeThread();
            return;
        }

        // bind types

        PrepareDefinition.Bind(pWDomain);

        // create named object

        ITestRoot pRootObject;
        ITestRoot.Create(out pRootObject, pWDomain);

        pRootObject.SetRootName("first test root");

        pRootObject.StoreData(Transaction.Store);

        pWDomain.InsertNamedObject(pRootObject.BuildLink(true), guidRootName, "first entry point", Transaction.Store);

        pWDomain.Execute(Transaction.Store);

        pRootObject.Dispose();

        // unbind types

        PrepareDefinition.Unbind();

        // disconnect

        pWDomain.ReleaseStorage(ulStorageId);
        pWDomain.DisconnectAll();
        pWDomain.Uninitialize();
        WDomain.Destroy(pWDomain);
        ThreadInit.UninitializeThread();
    }
}

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 catches next to no errors. This is done to make it easier to see the main function. It is not advisable to work this way in production.
DADL – AccessDefinition.dadl
#import <DataDefinition.bdtd>

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

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

Example – SimpleObject.cpp
Example – SimpleObject.cs
C++
C#
#include "stdafx.h"

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

#include "_data\AccessDefinition.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;

    // connect

    InitializeThread();

    WDomain* pWDomain = Domain_Create();
    if(FAILED(pWDomain->Initialize(&guidDomainId)))
    {
        Domain_Destroy(pWDomain);
        UninitializeThread();
        return -1;
    }

    if(FAILED(pWDomain->Connect(strServerAddress, usServerPort, NULL)))
    {
        pWDomain->Uninitialize();
        Domain_Destroy(pWDomain);
        UninitializeThread();
        return -1;
    }

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

    // bind types

    AccessDefinition::Bind(pWDomain);

    // open named object

    DataFoundation::ObjectId oiRootObject;

    pWDomain->QueryNamedObjectId(&guidRootName, 1, &oiRootObject);

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

    pRootObject->Load();
    pWDomain->Execute(Transaction::Load);

    // open the list for writing

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

    // create an add a new object

    ITestObject* pTestObject;
	ITestObject::Create(&pRootObject, pRootObject);

    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

    AccessDefinition::Unbind();

    // disconnect

    pWDomain->ReleaseStorage(ulStorageId);
    pWDomain->DisconnectAll();
    pWDomain->Uninitialize();
    Domain_Destroy(pWDomain);
    UninitializeThread();

    return 0;
}
using DataFoundationAccess;

class SimpleObject
{
    static readonly Guid guidRootName = new Guid("{56F8EB44-B7E3-4564-B9A6-22E5E1B9110C}");

    static void Main(string[] args)
    {
        String strServerAddress = args[0];
        UInt16 usServerPort = UInt16.Parse(args[1]);
        Guid guidDomainId = Guid.Parse(args[2]);

        UInt32 ulStorageId = 0;

        // connect

        ThreadInit.InitializeThread();

        WDomain pWDomain = WDomain.New();
        if(0 > (pWDomain.Initialize()))
        {
            WDomain.Delete(pWDomain);
            ThreadInit.UninitializeThread();
            return;
        }

        if(0 > (pWDomain.Connect(strServerAddress, usServerPort, guidDomainId, null)))
        {
            pWDomain.Uninitialize();
            WDomain.Delete(pWDomain);
            ThreadInit.UninitializeThread();
            return;
        }

        if(0 > (pWDomain.QueryStorage(ulStorageId, false, null)))
        {
            pWDomain.DisconnectAll();
            pWDomain.Uninitialize();
            WDomain.Delete(pWDomain);
            ThreadInit.UninitializeThread();
            return;
        }

        // bind types

        AccessDefinition.Bind(pWDomain);

        // open named object

        DataFoundation.ObjectId[] aoiRootObject;

        pWDomain.QueryNamedObjectId(new Guid[] { guidRootName }, out aoiRootObject);

        ITestRoot pRootObject;
        ITestRoot.Open(out pRootObject, aoiRootObject[0], pWDomain, Transaction.Load);

        pRootObject.Load(_ITestRoot.ALL_ATTRIBUTES, Transaction.Load);
        pWDomain.Execute(Transaction.Load);

        // open the list for writing

        TestObjectList pList;
        pRootObject.SetAllObjects(out pList);

        // create an add a new object

        ITestObject pTestObject;
        ITestObject.Create(out pTestObject, pRootObject);

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

        uint idx;
        pList.Insert(out idx, itm);

        pTestObject.SetText("something");
        pTestObject.SetNumber(343);

        pTestObject.StoreData(Transaction.Store);
        pRootObject.StoreData(Transaction.Store);

        pWDomain.Execute(Transaction.Store);

        pTestObject.Release();
        pRootObject.Release();

        // unbind types

        AccessDefinition.Unbind();

        // disconnect

        pWDomain.ReleaseStorage(ulStorageId);
        pWDomain.DisconnectAll();
        pWDomain.Uninitialize();
        WDomain.Delete(pWDomain);
        ThreadInit.UninitializeThread();
    }
}
© 2022 Mobiland AG