在访问数据源中的任何数据之前,首先需要建立一个对应的FDO Provider连接。本节中将介绍如何创建一个FDO Provider的连接。
1.1.1 数据源和Data Store
在创建一个FDO Provider连接之前,我们需要明白两个概念:数据源和Data Store。因为准确的来说,FDO的连接是针对Data Store的,而不是数据源。
在介绍FDO的基本概念时,我们提到Data Store表示包含零个或多个对象的集合。FDO连接是针对Data Store的,因为它是存储数据对象的地方。Data Store既可以保存在数据库中(如MySQL),也可以保存在文件(如SDF文件)。
Data Store和数据源可以是一对一或多对一的,即一个数据源包含一个或多个Data Store,这取决于FDO Provider的能力,例如:
l 对于OSGeo FDO Provider for ArcSDE的连接,在ArcSDE服务器使用Oracle数据库时,Data Store和数据源是一对一的。
l 对于OSGeo FDO Provider for MySQL的连接,Data Store和数据源是多对一的。
当Data Store和数据源是多对一时,连接可以经过一步或两步完成,将介绍不同连接方式的具体细节。
1.1.2 注册Provider
在安装FDO SDK时,FDO会自动注册FDO SDK中包含的所有FDO Provider。如果你想添加一种FDO SDK中没有包含的FDO Provider,就需要自己来注册这种FDO Provider,例如企业版FDO中没有包含OSGeo FDO Provider for GDAL,如果要在FDO企业版中使用GDAL Provider,就需要自己来注册FDO Provider。注册FDO Provider的方式有如下两种方式:
1) 修改文件providers.xml
在FDO的安装目录下有一个名称为“providers.xml”的XML文件,它包含了所有已经注册的FDO Provider,例如修改此文件加入如下两个省略号中间的内容,就可以注册GDAL Provider。
<?xml version="1.0" encoding="UTF-8" standalone="no" ?> <FeatureProviderRegistry> ...... <FeatureProvider> <Name>OSGeo.Gdal.3.4</Name> <DisplayName>OSGeo FDO Provider for GDAL</DisplayName> <Description>FDO Provider for GDAL</Description> <IsManaged>False</IsManaged> <Version>3.4.0.0</Version> <FeatureDataObjectsVersion>3.4.0.0</FeatureDataObjectsVersion> <LibraryPath>./GRFPProvider.dll</LibraryPath> </FeatureProvider> ...... </FeatureProviderRegistry> |
新加XML片段中每个元素的解释如下:
l Name:Provider的唯一命名,该命名需遵循<公司名/组织名称>.<Provider>.<版本号>的命名规则,例如:“Autodesk.Oracle.3.0”或“OSGeo.MySQL.3.0”。
l DisplayName:Provider显示给用户的名字,例如:“Autodesk FDO Provider for Oracle”、“OSGeo FDO Provider for SDF”或者本地化的字符串。
l Description:Provider的简要描述,例如:OSGeo FDO Provider for SDF的描述为“Read/write access to Autodesk's spatial database format, a file-based personal geodatabase that supports multiple features/attributes, spatial indexing, and file-locking.”
l Version:Provider的版本号,版本号一般遵循以下命名规则:<主版本号>.<次版本号>.<主Build号>.<次Build号>,例如:3.0.0.0。
l FDOVersion:Provider所在的FDO版本号,该版本号也遵循以下命名规则:<主版本号>.<次版本号>.<主Build号>.<次Build号>。例如:3.0.1.0。
l libraryPath:包含文件名在内的provider完整路径名,例如:<FDO SDK安装路径>/bin/FdoRdbms.dll.
l isManaged:用来标志Provider是托管的(Managed).NET FDO Provider,还是非托管的(Unmanaged)C++ Provider。
2) 编程方式
FDO API为Provider的注册和注销提供了接口,详情可参考文件Inc/Fdo/ClientServices/ProviderRegistry.h中的类FdoProviderRegistry。
1.1.3 建立连接
1.1.3.1 获取Provider的名称
要创建到一个Provider的连接,必须知道这个Provider的完整名称,Provider的命名需遵循如下格式:<公司名/组织名称>.<Provider>.<版本号>,其中<公司名/组织名称>和<Provider>是不变的,只有<版本号>随着provider的版本更新而变化。
如果你不知道Provider的名称,可以通过如下的代码获得当前所有注册的Provider的名称。
FdoPtr<FdoProviderRegistry> registry = (FdoProviderRegistry *)FdoFeatureAccessManager::GetProviderRegistry(); FdoProviderCollection * providers = registry->GetProviders(); FdoStringP displayName; FdoStringP internalName; FdoPtr<FdoProvider> provider; int count = providers->GetCount(); for(int i = 0; i < count; i++) { provider = providers->GetItem(i); internalName = provider->GetName(); displayName = provider->GetDisplayName(); ...... } |
1.1.3.2 一步连接和两步连接
在所有连接参数已知的情况下,只需一步就可完成建立FDO Provider连接。如果某些参数值只有在创建连接之后才可能获得,这时就需要使用两步连接。
1) 一步连接
用户设置了所需的连接参数,然后调用连接对象的Open()方法,如果返回的状态是FdoConnectionState_Open,一步连接就完成了。
2) 两步连接
用户设置了所需的连接参数,然后调用连接对象的Open()方法,如果返回的状态是FdoConnectionState_Pending,则连接尚未完成。首次调用Open()只是为了得到第二次调用Open()所需的参数,在得到所需的连接参数后,第二次调用Open()方法返回的状态应该就是FdoConnectionState_Open了。
例如创建对MySQL和ArcSDE Provider的连接时,既可以通过一步也可以通过两步完成。在第一步时,Data Store参数不是必需的,如果用户可以不指定该参数,在第一步连接完成后可以执行列举Data Store的命令得到数据源Data Store的列表,从而让用户为第二步的参数做出选择,此时连接需两步完成;如果用户在第一步指定了正确的Data Store参数值,那么可以一步完成创建连接。
1.1.3.3 创建连接示例
假设用户已经安装了MySQL,并且在安装时设置了系统管理员帐号名为“root”,密码为“test”,以下是创建MySQL Provider连接的基本步骤。
// 得到连接管理器实例 FdoPtr<FdoConnectionManager> connectMgr = (FdoConnectionManager *)FdoFeatureAccessManager::GetConnectionManager(); // 调用连接管理器的CreateConnection()方法,把Provider的名字作为该方法的参数 FdoPtr<FdoIConnection> fdoConnection = connectMgr->CreateConnection(L”OSGeo.MySQL.3.4”); //调用连接对象的GetConnectionInfo()方法来得到连接信息对象 FdoPtr<FdoIConnectionInfo> info = fdoConnection->GetConnection Info(); // 得到连接参数字典对象 FdoPtr<FdoIConnectionPropertyDictionary> ConnDict = info->GetConnectionProperties(); // 设置连接参数 ConnDict->SetProperty(L”Username”, L”root”); ConnDict->SetProperty(L”Password”, L”test”); ConnDict->SetProperty(L”Service”, L”localhost”); ConnDict->SetProperty(L”Datastore”, L”fdo_user”); // 打开连接 FdoConnectionState state = fdoConnection->Open(); |
以上四个SetProperty()方法等价于SetConnectionString(…),所以上面的代码等价于如下的代码。
FdoPtr<FdoConnectionManager> connectMgr = (FdoConnectionManager *)FdoFeatureAccessManager::GetConnectionManager(); FdoPtr<FdoIConnection> fdoConnection = connectMgr->CreateConnection(L”OSGeo.MySQL.3.4”); // 设置连接字符串 fdoconnection->SetConnectionString( L”Username=root;Password=test;Service=localhost;Datastore=fdo_user”); // 打开连接 FdoConnectionState state = fdoConnection->Open(); |
每种不同类型的Provider使用了不同的连接参数,例如创建一个MySQL Provider的连接需要参数Username、Password、Service、Datastore,而创建一个SDF Provider的连接需要参数File、Readonly。如果想知道创建一个Provider的连接需要哪些参数,以及这些参数的名称、默认值、是否可选等信息,可以通过连接参数字典类FdoIConnectionPropertyDictionary获得这些信息。假设ConnDict是类FdoIConnectionPropertyDictionary的一个实例,使用如下的代码可以枚举Provider的所有连接参数,以及这些参数的属性。
FdoInt32 count = 0; FdoString ** names = NULL; FdoStringP name; FdoStringP localname; FdoStringP val; FdoStringP defaultVal; bool isRequired = false; bool isProtected = false; bool isFilename = false; bool isFilepath = false; bool isDatastorename = false; bool isEnumerable = false; FdoInt32 enumCount = 0; FdoString ** enumNames = NULL; FdoStringP enumName; names = ConnDict->GetPropertyNames(count); for(int i = 0; i < count; i++) { name = names[i]; val = dict->GetProperty(name); defaultVal = dict->GetPropertyDefault(name); localname = dict->GetLocalizedName(name); isRequired = dict->IsPropertyRequired(name); isProtected = dict->IsPropertyProtected(name); isFilename = dict->IsPropertyFileName(name); isFilepath = dict->IsPropertyFilePath(name); isDatastorename = dict->IsPropertyDatastoreName(name); isEnumerable = dict->IsPropertyEnumerable(name); if (isEnumerable) { if (isRequired) { enumNames = dict->EnumeratePropertyValues(name, enumCount); for(int j = 0; j < enumCount; j++) { enumName = enumNames[j]; } } } } |
调用连接参数字典的GetLocalizedName()方法得到要显示给用户的本地化属性名,此时需要GetPropertyNames()函数返回的连接参数名称作为参数。调用IsPropertyRequired()方法可以得知某连接参数是可选还是必选。调用IsPropertyProtected()方法来决定某连接参数是不是受保护的,例如密码,此时的密码输入文本框不应显示明码。
调用IsPropertyEnumerable()和IsRequired()方法来决定是否需要调用EnumeratePropertyValues()方法以得到合法值列表。只有这两个方法同时返回TRUE才能调用EnumeratePropertyValues()方法,否则如果此时没有挂起的连接,该方法会抛出异常。
从上面的示例代码可以看出,EnumeratePropertyValues()方法有两个参数,属性名和一个整数引用,返回字符串数组。整数引用的值说明了返回的数组包含字符串的数量。如果属性是可枚举的,就把返回的选项列表显示给用户;如果属性不是可枚举的,可以把GetProperty()或GetPropertyDefault()的返回值显示给用户。
下面我们再来看一个二次连接的例子,如下的代码首先创建了一个MySQL Provider连接,然后创建了一个新的Data Store,最后连接到这个Data Store。
// 创建连接 FdoPtr<FdoConnectionManager> connectMgr = (FdoConnectionManager *)FdoFeatureAccessManager::GetConnectionManager(); FdoPtr<FdoIConnection> fdoConnection = connectMgr->CreateConnection(L”OSGeo.MySQL.3.4”);
// 第一次打开连接 fdoconnection->SetConnectionString(L”Username=root;Password=test;Service=localhost”); FdoConnectionState state = fdoConnection->Open();
// 创建新的Data Store FdoPtr<FdoICreateDataStore> createDataStoreCmd = dynamic_cast<FdoICreateDataStore *> (fdoConnection->CreateCommand(FdoCommandType_CreateDataStore)); FdoPtr<FdoIDataStorePropertyDictionary> createDsDict = createDataStoreCmd->GetDataStoreProperties(); createDsDict->SetProperty(L”DataStore”, L”fdo_user”); createDataStoreCmd->Execute();
// 第二次打开连接 state = fdoConnection->Open(); |
第一次调用连接对象的Open()方法,返回的状态是FdoConnectionState_Pending,这时可以用挂起的连接创建Data Store。Data store创建之后,再次调用连接对象的Open()方法,返回的就应该是FdoConnectionState_Open了。