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;
}