Главная страница Обратная связь Карта сайта


  

Демо-версия
сайт и интерфейс

Документация
все о системе

  

Служба поддержки
+7 (3412) 511419

Использование ADOX

Применительно к MSAccess, c примерами на C#

Опубликовано в журнале «Алгоритм»

Введение

Полное название ADOX — Microsoft ActiveX Data Objects Extensions for Data Definition Language and Security. Другими словами, ADOX — это COM-библиотека, являющаяся дополнением к библиотеке ADO и предназначенная для управления структурой и безопасностью базы.

ADOX позволяет как получить информацию об объектах, так и управлять ими — создавать, изменять и удалять. Объекты, доступные ADOX — это таблицы (Tables), их поля (Columns), ключи (Keys) и индексы (Indexes), процедуры (Procedures) и вьюшки (Views), а также пользователи (Users) и группы (Groups). ADOX не доступны такие объекты MS Access как: формы, отчеты, страницы, макросы и модули.

Данная статья ориентирована на использование ADOX для баз формата MDB. Вообще говоря, ADOX предназначен для работы не только с базами MS Access, но наиболее полно поддержка ADOX реализована в OLE DB Provider for MS Jet. Подробнее см. Provider Support for ADOX.

См. также: Описание ADOX в MSDN, CodeGuru: ADOX in Detail, ADO Provider Properties and Settings.

Соединение с базой

Соединение с базой устанавливается либо в результате создания базы вызовом Catalog.Create (см. ниже), либо «вручную» — для существующей базы. В обоих случаях используется одна и та же строка соединения (connection string).

Строка соединения

public static string GetConnectionString(
        string ADatabasePath, string AShareMode, string APassword) {
       
    StringBuilder connText = new StringBuilder();
    connText.Append("Provider=Microsoft.Jet.OLEDB.4.0;");
    connText.Append("Data Source=\"").Append(ADatabasePath).Append("\";");
    connText.Append("Mode=").Append(AShareMode).Append(";");
    if (APassword != string.Empty)
        connText.Append("Jet OLEDB:Database Password=\"").Append(APassword).Append("\";");
    connText.Append("Jet OLEDB:Engine Type=5;");
    connText.Append("Jet OLEDB:Encrypt Database=True;");
    connText.Append("User ID=\"Admin\";");
   
    return connText.ToString();
}

Про Provider-Specific Connection Parameters см.: Microsoft OLE DB Provider for Microsoft Jet.

См. также: ADO Connection Strings, www.connectionstrings.com

Открытие/закрытие соединения

Здесь и далее предполагается, что в проекте уже установлены ссылки на ADO и ADOX: в Solution Explorer правой кнопкой на References, пункт Add Reference, вкладка COM, выбрать строку Microsoft ADO Ext. 2.7 for DDL and Security, нажать Select, выбрать строку Microsoft ActiveX Data Objects 2.8 Library, нажать Select, затем OK. Таким образом, c объектами ADO и ADOX мы будем использовать через раннее связывание. Раннее связывание выбрано с целью сделать код примеров более нагладным. Работа через позднее связывание, конечно, также возможна.

Пусть у нас в каком-либо модуле будут две ссылки:

static ADODB.Connection g_adodbConnection; 
static ADOX.Catalog g_adoxCatalog;

Собственно открытие соединения:

public static ADOX.Catalog OpenAdoxConnection(string connectionString) { 
try {
g_adodbConnection = new ADODB.ConnectionClass();
g_adodbConnection.Open(connectionString, null, null, 0);
g_adoxCatalog = new ADOX.CatalogClass();
g_adoxCatalog.ActiveConnection = g_adodbConnection;
} catch (Exception ex) {
throw new Exception("Failed to open connection", ex);
}
return g_adoxCatalog;
}

Закрытие соединения:

public static void CloseAdoxConnection() { 
try {
if (g_adodbConnection != null) {
g_adoxCatalog = null;
g_adodbConnection.Close();
g_adodbConnection = null;
}
}
catch { //suppress exceptions
}
}

Создание базы данных

Пример создания пустой базы данных .MDB (MSAccess) с помощью метода Create() ADOX.Catalog (см. также: HOW TO: Create an Access Database Using ADOX and Visual C# .NET):

Обратите внимание на закрытие соединения. Если вы пишите на C++ или VB6, то Close можно не вызывать, поскольку освобождение COM-объекта Catalog приведет к закрытию соединения. Но под .NET между присваиванием adoxCatalog = null и освобождением объекта может пройти значительное время, в течение которого .MDB-файл будет занят...

При создании пустой базы через ADOX: размер файла .MDB — 64Кб, база содержит 4 системных таблицы (MSysACEs, MSysObjects, MSysQueries, MSysRelationships). При создании пустой базы из MS Access 2003: размер базы — 92Кб, база содержит 4 системных таблицы плюс одну таблицу Access (MSysAccessObjects).

Таблицы

См. также: Working with Tables in Microsoft Access, How To Change an Access Table Name Programmatically.

Перебор списка таблиц/полей

Пример перебора списка таблиц:

foreach (ADOX.Table adoxTable in adoxCat.Tables) {
    string tableName = adoxTable.Name;
    if (adoxTable.Type == "LINK") {  // Это внешняя таблица
        //...
    } else
    if (adoxTable.Type == "TABLE") {  // Это обычная таблица Access
        // Перебор полей таблицы
        foreach (ADOX.Column adoxCol in adoxTable.Columns) {
            string colName = adoxCol.Name;
            //...
        }
    }
} //foreach adoxTable

Типы таблиц MSAccess (свойство ADOX.Table.Type):

Тип таблицы Описание
ACCESS TABLE An Access system table
LINK A linked table from a non-ODBC data source
PASS-THROUGH A linked table through an ODBC data source
SYSTEM TABLE A Jet system table
TABLE A table developed by or for your application
VIEW A table from a row-returning, nonparameterized query

Лирическое отступление… Существует еще по крайней мере два способа получения списка таблиц (но уже без использования ADOX). Первый — с использованием системной таблицы MSysObjects:

SELECT MSysObjects.Name FROM MSysObjects
WHERE (((MSysObjects.Flags)=0) AND ((MSysObjects.Type)=1))
ORDER BY MSysObjects.Name

Второй способ — через ADO.NET:

OleDbConnection.GetOleDbSchemaTable(OleDbSchemaGuid.Tables, 
    new object[] {null, null, null, "TABLE"});
Свойства таблицы

Объект ADOX.Table содержит коллекцию Properties со следующими свойствами:

Свойство Тип Описание
Temporary Table adBoolean Показывает, является ли таблица временной. Для MS Jet этот параметр всегда равен False
Jet OLEDB:Create Link adBoolean
Jet OLEDB:Cache Link Name/Password adBoolean Показывает, нужно ли кешировать информацию по аутентификации к источнику данных ODBC — при создании линкованной таблицы
Jet OLEDB:Remote Table Name adBSTR Имя таблицы в линкуемой базе — при создании линкованной таблицы
Jet OLEDB:Link Provider String adBSTR
Jet OLEDB:Link Datasource adBSTR
Jet OLEDB:Exclusive Link adBoolean При создании линкованной таблицы — показывает, должен ли источник данных открываться в монопольном режиме
Jet OLEDB:Table Validation Text adBSTR
Jet OLEDB:Table Validation Rule adBSTR
Jet OLEDB:Table Hidden In Access adBoolean
Свойства поля

Для объекта Column в коллекции Properties доступны следующие свойства:

Свойство Тип Описание
Autoincrement adBoolean Показывает, увеличивается ли значение поля автоматически при добавлении новой записи
Default adVariant Значение поля по умолчанию
Description adBSTR Описание поля
Nullable adBoolean Показывает, может ли поле содержать значение NULL
Fixed Length adBoolean Показывает, является ли длина поля фиксированной или переменной
Seed adInteger
Increment adInteger Для счетчика — величина, на которую увеличивается значение счетчика при создании новой записи
Jet OLEDB:Column Validation Text adBSTR
Jet OLEDB:Column Validation Rule adBSTR Выражение, которое должно быть вычислено для проверки значения перед присваиванием
Jet OLEDB:IISAM Not Last Column adBoolean
Jet OLEDB:AutoGenerate adBoolean
Jet OLEDB:One BLOB per Page adBoolean
Jet OLEDB:Compressed UNICODE Strings adBoolean Используется ли сжатие Unicode при хранении строк
Jet OLEDB:Allow Zero Length adBoolean Для строковых полей: показывает, допустимы ли значения-строки нулевой длины
Jet OLEDB:Hyperlink adBoolean Для Memo-поля: показывает, является ли поле гиперссылкой

Создание таблицы

// Создаем объект-таблицу
ADOX.Table adoxTable = new ADOX.TableClass();
adoxTable.ParentCatalog = adoxCat;
adoxTable.Name = "Table1";

// Создаем поля и наполняем коллекцию Columns
// 1. Простой способ: просто указываем имя, тип и размер
adoxTable.Columns.Append("Column1", ADOX.DataTypeEnum.adInteger, 0);    // Column1 Long
adoxTable.Columns.Append("Column2", ADOX.DataTypeEnum.adVarWChar, 50);  // Column2 Text(50)

// 2. Добавление через создание объекта Column
ADOX.Column adoxColumn3 = new ADOX.ColumnClass();
adoxColumn3.ParentCatalog = adoxCat;
adoxColumn3.Name = "Column3";
adoxColumn3.Type = ADOX.DataTypeEnum.adInteger;
adoxTable.Columns.Append(adoxColumn3, ADOX.DataTypeEnum.adInteger, 0);

// 3. Добавление поля типа AutoIncrement
ADOX.Column adoxColumn4 = new ADOX.ColumnClass();
adoxColumn4.ParentCatalog = adoxCat;
adoxColumn4.Name = "Column4";
adoxColumn4.Type = ADOX.DataTypeEnum.adInteger;
adoxColumn4.Properties["AutoIncrement"].Value = true;
adoxTable.Columns.Append(adoxColumn4, ADOX.DataTypeEnum.adInteger, 0);

// Вносим изменения в базу
adoxCat.Tables.Append(adoxTable);

См. также: How To Create a Table with Primary Key Through ADOX.

Линковка внешних таблиц

См. ACC2000: How to Use ADOX to Create and Refresh Linked Jet Tables.

public static void CreateLinkedTable(
        ADOX.Catalog adoxCat,
        string ATableName, string ARemoteTableName, string ADatabasePath,
        string AProviderString)
{
    ADOX.Table adoxTable = new ADOX.TableClass();
    adoxTable.ParentCatalog = adoxCat;
    adoxTable.Name = ATableName;
    adoxTable.Properties["Jet OLEDB:Link Datasource"].Value = ADatabasePath;
    adoxTable.Properties["Jet OLEDB:Remote Table Name"].Value = ARemoteTableName;
    adoxTable.Properties["Jet OLEDB:Create Link"].Value = true;
    if (AProviderString != null)
        adoxTable.Properties["Jet OLEDB:Link Provider String"].Value = AProviderString;
    adoxCat.Tables.Append(adoxTable);
}
Обновление связи с внешним файлом
public static void UpdateLinkedTable(
    ADOX.Catalog adoxCat,
    string ATableName, string ADatabasePath,
    string ADatabasePassword)
{
    ADOX.Table adoxTable = adoxCat.Tables[ATableName];
    if (adoxTable == null)
        throw new WarningException("Table '" + ATableName + "' not found.");
    adoxTable.Properties["Jet OLEDB:Link Datasource"].Value = ADatabasePath;
    if (ADatabasePassword != null)
        adoxTable.Properties["Jet OLEDB:Link Provider String"].Value = "MS Access;PWD=" + ADatabasePassword + ";";
}

Удаление таблиц

adoxCat.Tables.Delete("Table1");

Индексы

Перебор списка индексов таблицы

ADOX.Table adoxTable = adoxCat.Tables["Table1"];
foreach (ADOX.Index adoxIndex in adoxTable.Indexes) {
    string indexName = adoxIndex.Name;
    ...
}

Свойства индекса

Свойство Тип Описание
Auto-Update adBoolean Показывает, поддерживается ли индекс автоматически при изменениях в таблице. Для MS Jet — всегда True
Clustered adBoolean Показывает, является ли индекс кластеризованным. Для MS Jet — всегда False
Fill Factor adInteger
Initial Size adInteger Объем структуры на момент создания. Для MS Jet — всегда 4096 байт на страницу
NULL Collation adInteger
NULL Keys adInteger
Primary Key adBoolean Показывает, представляет ли данный индекс первичный ключ таблицы
Sort Bookmarks adBoolean
Index Type adInteger Тип индекса. Для MS Jet это всегда 1, что означает — B+-дерево
Unique adBoolean Показывает, должны ли ключи индекса быть уникальными
Temporary Index adBoolean Показывает, является ли индекс временным. Для MS Jet этот параметр всегда равен False

Запросы

Под запросами подразумеваются объекты типа PROCEDURE и VIEW. Microsoft Jet engine не делает различий между этими двумя типами, но Microsoft Jet OLE DB 4.0 provider проводит это разграничение. Как правило, все НЕ-параметризированные SELECT-запросы относятся к VIEW, все остальные запросы рассматриваются как PROCEDURE. Но бывают и ошибки — см. PRB: Non-Parameterized SELECT Query Appears in ADO Procedures Collection.

Перебор списка запросов

Перебор коллекции Views:

foreach (ADOX.View adoxView in adoxCat.Views) { 
    string viewname = adoxView.Name;
    ADODB.Command adoCommand = (ADODB.Command) adoxView.Command;
    string cmdtext = adoCommand.CommandText();
    //...
}

Перебор коллекции Procedures:

foreach(ADOX.Procedure adoxProc in adoxCat.Procedures) {
    string procname = adoxProc.Name;
    ADODB.Command adoCommand = (ADODB.Command) adoxProc.Command;
    string cmdtext = adoCommand.CommandText;
    //...
}

Создание запроса

Создание простого запроса:

ADODB.Command cmdobj = new ADODB.CommandClass();
cmdobj.CommandText = "SELECT * FROM Table1";
adoxCat.Views.Append("Query1", cmdobj);

Создание параметризованного запроса:

ADODB.Command cmdobj2 = new ADODB.CommandClass();
cmdobj2.CommandText = "SELECT * FROM Table1 WHERE Column1 = [_Param1]";
cmdobj2.CreateParameter("_Param1",
    ADODB.DataTypeEnum.adInteger, ADODB.ParameterDirectionEnum.adParamInput, 0, null);
adoxCat.Procedures.Append("Query2", cmdobj2);

См. также: Creating and Modifying Stored Queries in Microsoft Access Databases with ADOX, PRB: Views Created Using ADOX Are Not Visible in Access 2000

Удаление запросов

adoxCat.Views.Delete("Query1");
adoxCat.Procedures.Delete("Query2");

Сжатие/восстановление базы (Compact/Repair)

В БД Access при операциях удаления соответствующие строки, таблицы и другие объекты на самом деле НЕ удаляются, а лишь помечаются как удаленные. В результате объем файла БД монотонно растет.

См. также: FAQ / MS Access / Сжатие БД [SQL.Ru]

Оценка объема, высвобождаемого при сжатии

При наличии открытого ADODB-соединения всегда можно сказать — нужно ли сжимать базу. Свойство «Jet OLEDB:Compact Reclaimed Space Amount» соединения дает количество байт, высвобождаемых при сжатии.

public static int GetCompactReclaimedSpaceAmount(string connectionString) {
    // Открытие соединения
    ADODB.Connection connobj = new ADODB.ConnectionClass();
    connobj.Open(connectionString, null, null, 0);
    // Получение значения свойства "Jet OLEDB:Compact Reclaimed Space Amount"
    int value = 0;
    try {
        value = (int) connobj.Properties["Jet OLEDB:Compact Reclaimed Space Amount"].Value;
    } finally {
        connobj.Close();
    }
    return value;
}

Собственно сжатие

Из ADOX функция сжатия/восстановления недоступна, но эту операцию можно выполнить с помощью библиотеки Jet and Replication Objects (JRO). Перед выполнением сжатия все соединения с базой должны быть закрыты.

public static void CompactDatabase(string ADatabasePath, string ADatabasePassword) {
    string TempFileName = Path.GetTempFileName();
    if (File.Exists(TempFileName))
        File.Delete(TempFileName);  // Удаляем файл нулевой длины, созданный Path.GetTempFileName() 
    try { 
        // Сжатие из файла ADatabasePath в файл TempFileName
        CompactDatabaseJro(
            GetCompactConnectionString(ADatabasePath, ADatabasePassword),
            GetCompactConnectionString(TempFileName, ADatabasePassword)); 
        File.Copy(TempFileName, ADatabasePath, true); 
    } finally { 
        // Удаляем временный файл
        if (File.Exists(TempFileName))
            File.Delete(TempFileName); 
    }        
}

private static string GetCompactConnectionString(string databasePath, string password) { 
    StringBuilder connText = new StringBuilder(); 
    connText.Append("Provider=Microsoft.Jet.OLEDB.4.0;"); 
    connText.Append("Data Source=\"").Append(databasePath).Append("\";"); 
    if (Common.g_Password != string.Empty)
        connText.Append("Jet OLEDB:Database Password=\"").Append(password).Append("\";");
    connText.Append("Jet OLEDB:Engine Type=5;"); 
    connText.Append("Jet OLEDB:Encrypt Database=True;"); 
    return connText.ToString(); 
}

private static void CompactDatabaseJro(string SrcConnectionString, string DestConnectionString) {
    Type typeJROJetEngine = Type.GetTypeFromProgID("JRO.JetEngine");
    object jro = Activator.CreateInstance(typeJROJetEngine);
    object[] parameter = new object[2];
    parameter[0] = SrcConnectionString;
    parameter[1] = DestConnectionString;
    typeJROJetEngine.InvokeMember("CompactDatabase", BindingFlags.InvokeMethod, null, jro, parameter);
    jro = null;
}

Никита Зимин,
ведущий разработчик компании «Деловые программы»

Вернуться к списку статей

Rambler's Top100 CMS List: Обзор систем управления сайтами и программ для создания сайтов
© 2005–2012 ООО «Компания «Деловые программы»