You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
884 lines
32 KiB
C#
884 lines
32 KiB
C#
using System.Collections.Generic;
|
|
using System.Data.Common;
|
|
using System.Data.Jet.JetStoreSchemaDefinition;
|
|
using System.Diagnostics;
|
|
|
|
namespace System.Data.Jet
|
|
{
|
|
public class AdoxSchema : SchemaProvider
|
|
{
|
|
private readonly bool _naturalOnly;
|
|
private readonly dynamic _connection;
|
|
private readonly dynamic _catalog;
|
|
|
|
public AdoxSchema(JetConnection connection, bool naturalOnly)
|
|
: this(connection)
|
|
{
|
|
_naturalOnly = naturalOnly;
|
|
}
|
|
|
|
public AdoxSchema(JetConnection connection)
|
|
{
|
|
_connection = new ComObject("ADODB.Connection");
|
|
|
|
try
|
|
{
|
|
var connectionString = GetOleDbConnectionString(connection.ActiveConnectionString);
|
|
_connection.Open(connectionString);
|
|
|
|
_catalog = new ComObject("ADOX.Catalog");
|
|
|
|
try
|
|
{
|
|
_catalog.ActiveConnection = _connection;
|
|
}
|
|
catch
|
|
{
|
|
_catalog.Dispose();
|
|
throw;
|
|
}
|
|
}
|
|
catch
|
|
{
|
|
_connection.Dispose();
|
|
throw;
|
|
}
|
|
}
|
|
|
|
private static string GetOleDbConnectionString(string fileNameOrConnectionString)
|
|
{
|
|
if (JetStoreDatabaseHandling.IsConnectionString(fileNameOrConnectionString) &&
|
|
JetConnection.GetDataAccessProviderType(fileNameOrConnectionString) == DataAccessProviderType.OleDb)
|
|
{
|
|
return fileNameOrConnectionString;
|
|
}
|
|
|
|
var filePath = JetStoreDatabaseHandling.ExpandFileName(JetStoreDatabaseHandling.ExtractFileNameFromConnectionString(fileNameOrConnectionString));
|
|
var connectionString = JetConnection.GetConnectionString(filePath, DataAccessProviderType.OleDb);
|
|
|
|
if (JetStoreDatabaseHandling.IsConnectionString(fileNameOrConnectionString) &&
|
|
JetConnection.GetDataAccessProviderType(fileNameOrConnectionString) == DataAccessProviderType.Odbc)
|
|
{
|
|
var oldCsb = new DbConnectionStringBuilder(true) {ConnectionString = fileNameOrConnectionString};
|
|
var newCsb = new DbConnectionStringBuilder {ConnectionString = connectionString};
|
|
|
|
newCsb.SetUserId(oldCsb.GetUserId(DataAccessProviderType.Odbc), DataAccessProviderType.OleDb);
|
|
newCsb.SetPassword(oldCsb.GetPassword(DataAccessProviderType.Odbc), DataAccessProviderType.OleDb);
|
|
newCsb.SetSystemDatabase(oldCsb.GetSystemDatabase(DataAccessProviderType.Odbc), DataAccessProviderType.OleDb);
|
|
newCsb.SetDatabasePassword(oldCsb.GetDatabasePassword(DataAccessProviderType.Odbc), DataAccessProviderType.OleDb);
|
|
|
|
connectionString = newCsb.ConnectionString;
|
|
}
|
|
|
|
return connectionString;
|
|
}
|
|
|
|
public override void EnsureDualTable()
|
|
{
|
|
using var tables = _catalog.Tables;
|
|
|
|
try
|
|
{
|
|
using var table = tables["#Dual"];
|
|
}
|
|
catch
|
|
{
|
|
const string sqlQueries = @"CREATE TABLE `#Dual` (`ID` INTEGER NOT NULL CONSTRAINT `PrimaryKey` PRIMARY KEY);
|
|
INSERT INTO `#Dual` (`ID`) VALUES (1);
|
|
ALTER TABLE `#Dual` ADD CONSTRAINT `SingleRecord` CHECK (`ID` = 1)";
|
|
var connection = _connection.InnerConnection;
|
|
var queries = sqlQueries.Split(';');
|
|
|
|
foreach (var query in queries)
|
|
{
|
|
using var command = connection.CreateCommand();
|
|
command.CommandText = query;
|
|
command.ExecuteNonQuery();
|
|
}
|
|
|
|
try
|
|
{
|
|
tables.Refresh();
|
|
using var table = tables["#Dual"];
|
|
using var properties = table.Properties;
|
|
using var property = properties["Jet OLEDB:Table Hidden In Access"];
|
|
property.Value = true;
|
|
}
|
|
catch
|
|
{
|
|
// ignored
|
|
}
|
|
}
|
|
}
|
|
|
|
public override DataTable GetTables()
|
|
{
|
|
var dataTable = SchemaTables.GetTablesDataTable();
|
|
|
|
// Only necessary, if ADOX is used in conjunction with ODBC.
|
|
// var procedureSet = new HashSet<string>();
|
|
// using var procedures = _catalog.Procedures;
|
|
// if (procedures != null)
|
|
// {
|
|
// var procedureCount = procedures.Count;
|
|
//
|
|
// for (var i = 0; i < procedureCount; i++)
|
|
// {
|
|
// using var procedure = procedures[i];
|
|
//
|
|
// var procedureName = (string) procedure.Name;
|
|
// procedureSet.Add(procedureName);
|
|
// }
|
|
// }
|
|
|
|
using var tables = _catalog.Tables;
|
|
var tableCount = tables.Count;
|
|
|
|
for (var i = 0; i < tableCount; i++)
|
|
{
|
|
using var table = tables[i];
|
|
using var properties = table.properties;
|
|
|
|
var tableName = (string)table.Name;
|
|
|
|
// Depending on the provider (ODBC or OLE DB) used, the Tables collection might contain VIEWs
|
|
// that take parameters, which makes them procedures.
|
|
// We make sure here, that we exclude any procedures from the returned table list.
|
|
// if (!procedureSet.Contains(tableName))
|
|
{
|
|
var tableType = (string) table.Type;
|
|
|
|
Debug.Assert(
|
|
tableType == "TABLE" ||
|
|
tableType == "LINK" ||
|
|
tableType == "SYSTEM TABLE" ||
|
|
tableType == "ACCESS TABLE" ||
|
|
tableType == "VIEW" ||
|
|
tableType == "TEMPORARY TABLE");
|
|
|
|
if (IsInternalTableByName(tableName))
|
|
{
|
|
tableType = "INTERNAL TABLE";
|
|
}
|
|
else if (IsSystemTableByName(tableName))
|
|
{
|
|
tableType = "SYSTEM TABLE";
|
|
}
|
|
else if (tableType == "TABLE" ||
|
|
tableType == "LINK")
|
|
{
|
|
tableType = "BASE TABLE";
|
|
}
|
|
|
|
var validationRule = GetPropertyValue<string>(properties, "Jet OLEDB:Table Validation Rule");
|
|
validationRule = string.IsNullOrEmpty(validationRule)
|
|
? null
|
|
: validationRule;
|
|
|
|
var validationText = GetPropertyValue<string>(properties, "Jet OLEDB:Table Validation Text");
|
|
validationText = string.IsNullOrEmpty(validationText)
|
|
? null
|
|
: validationText;
|
|
|
|
dataTable.Rows.Add(
|
|
tableName,
|
|
tableType,
|
|
validationRule,
|
|
validationText);
|
|
}
|
|
}
|
|
|
|
dataTable.AcceptChanges();
|
|
return dataTable;
|
|
}
|
|
|
|
public override DataTable GetColumns()
|
|
{
|
|
var dataTable = SchemaTables.GetColumnsDataTable();
|
|
|
|
// There is no way to get the ordinal position of a column with ADOX. Looks like someone at Microsoft just forgot to
|
|
// implement it.
|
|
// Therefore, either DAO has to be used, or ADOX together with the OpenSchema()
|
|
// method.
|
|
|
|
Dictionary<(string TableName, string ColumnName), int> ordinalPositions = null;
|
|
|
|
if (!_naturalOnly)
|
|
{
|
|
using (var recordset = _connection.OpenSchema(SchemaEnum.adSchemaColumns))
|
|
{
|
|
ordinalPositions = new Dictionary<(string TableName, string ColumnName), int>();
|
|
|
|
using var fields = recordset.Fields;
|
|
using var tableNameField = fields["TABLE_NAME"];
|
|
using var columnNameField = fields["COLUMN_NAME"];
|
|
using var ordinalPositionField = fields["ORDINAL_POSITION"];
|
|
|
|
recordset.MoveFirst();
|
|
|
|
while (!recordset.EOF)
|
|
{
|
|
var tableName = (string) tableNameField.Value;
|
|
var columnName = (string) columnNameField.Value;
|
|
var ordinalPosition = (int) ordinalPositionField.Value - 1;
|
|
|
|
ordinalPositions.Add((tableName, columnName), ordinalPosition);
|
|
recordset.MoveNext();
|
|
}
|
|
|
|
recordset.Close();
|
|
}
|
|
}
|
|
|
|
using var tables = _catalog.Tables;
|
|
var tableCount = tables.Count;
|
|
|
|
for (var i = 0; i < tableCount; i++)
|
|
{
|
|
using var table = tables[i];
|
|
var tableName = (string)table.Name;
|
|
|
|
using var columns = table.Columns;
|
|
var columnCount = columns.Count;
|
|
|
|
for (var j = 0; j < columnCount; j++)
|
|
{
|
|
using var column = columns[j];
|
|
using var properties = column.Properties;
|
|
|
|
var columnName = (string)column.Name;
|
|
var attributes = (ColumnAttributesEnum) column.Attributes;
|
|
var ordinalPosition = ordinalPositions?[(tableName, columnName)] ?? j;
|
|
var dataType = (DataTypeEnum)column.Type;
|
|
|
|
// This in inpercise in ADOX, especially for Views. The "Nullable" property is even worse.
|
|
var nullable = (attributes & ColumnAttributesEnum.adColNullable) == ColumnAttributesEnum.adColNullable;
|
|
|
|
var isIdentity = dataType == DataTypeEnum.adInteger &&
|
|
GetPropertyValue<bool>(properties, "Autoincrement");
|
|
var seed = isIdentity
|
|
? (int?) GetPropertyValue<int>(properties, "Seed")
|
|
: null;
|
|
var increment = isIdentity
|
|
? (int?) GetPropertyValue<int>(properties, "Increment")
|
|
: null;
|
|
var dataTypeString = GetDataTypeString(dataType, isIdentity);
|
|
var numericPrecision = dataType == DataTypeEnum.adDecimal || dataType == DataTypeEnum.adNumeric
|
|
? (int?)column.Precision
|
|
: null;
|
|
var numericScale = dataType == DataTypeEnum.adDecimal || dataType == DataTypeEnum.adNumeric
|
|
? (int?)(byte)column.NumericScale
|
|
: null;
|
|
|
|
var size = (int) column.DefinedSize;
|
|
var length = GetMaxLength(dataType, size);
|
|
|
|
var defaultValue = GetPropertyValue<string>(properties, "Default");
|
|
|
|
var validationRule = GetPropertyValue<string>(properties, "Jet OLEDB:Column Validation Rule");
|
|
validationRule = string.IsNullOrEmpty(validationRule)
|
|
? null
|
|
: validationRule;
|
|
|
|
var validationText = GetPropertyValue<string>(properties, "Jet OLEDB:Column Validation Text");
|
|
validationText = string.IsNullOrEmpty(validationText)
|
|
? null
|
|
: validationText;
|
|
|
|
dataTable.Rows.Add(
|
|
tableName,
|
|
columnName,
|
|
ordinalPosition,
|
|
dataTypeString,
|
|
nullable,
|
|
length,
|
|
numericPrecision,
|
|
numericScale,
|
|
defaultValue,
|
|
validationRule,
|
|
validationText,
|
|
seed,
|
|
increment);
|
|
}
|
|
}
|
|
|
|
dataTable.AcceptChanges();
|
|
return dataTable;
|
|
}
|
|
|
|
public override DataTable GetIndexes()
|
|
{
|
|
var dataTable = SchemaTables.GetIndexesDataTable();
|
|
|
|
using var tables = _catalog.Tables;
|
|
var tableCount = tables.Count;
|
|
|
|
for (var i = 0; i < tableCount; i++)
|
|
{
|
|
using var table = tables[i];
|
|
var tableName = (string)table.Name;
|
|
|
|
using var indexes = table.Indexes;
|
|
var indexCount = (int) indexes.Count;
|
|
|
|
for (var j = 0; j < indexCount; j++)
|
|
{
|
|
using var index = indexes[j];
|
|
|
|
var indexName = (string) index.Name;
|
|
var indexNulls = (AllowNullsEnum) index.IndexNulls;
|
|
var isPrimaryKey = (bool) index.PrimaryKey;
|
|
var isUnique = (bool) index.Unique;
|
|
var isNullable = indexNulls != AllowNullsEnum.adIndexNullsDisallow;
|
|
var ignoreNulls = isNullable && indexNulls != AllowNullsEnum.adIndexNullsAllow;
|
|
|
|
using var properties = index.Properties;
|
|
|
|
string indexType;
|
|
if (isPrimaryKey)
|
|
{
|
|
indexType = "PRIMARY";
|
|
}
|
|
else if (isUnique)
|
|
{
|
|
indexType = "UNIQUE";
|
|
}
|
|
else
|
|
{
|
|
indexType = "INDEX";
|
|
}
|
|
|
|
dataTable.Rows.Add(
|
|
tableName,
|
|
indexName,
|
|
indexType,
|
|
isNullable,
|
|
ignoreNulls);
|
|
}
|
|
}
|
|
|
|
dataTable.AcceptChanges();
|
|
return dataTable;
|
|
}
|
|
|
|
public override DataTable GetIndexColumns()
|
|
{
|
|
var dataTable = SchemaTables.GetIndexColumnsDataTable();
|
|
|
|
using var tables = _catalog.Tables;
|
|
var tableCount = tables.Count;
|
|
|
|
for (var i = 0; i < tableCount; i++)
|
|
{
|
|
using var table = tables[i];
|
|
var tableName = (string)table.Name;
|
|
|
|
using var indexes = table.Indexes;
|
|
var indexCount = (int) indexes.Count;
|
|
|
|
for (var j = 0; j < indexCount; j++)
|
|
{
|
|
using var index = indexes[j];
|
|
|
|
var indexName = (string) index.Name;
|
|
|
|
using var columns = index.Columns;
|
|
var columnCount = (int) columns.Count;
|
|
|
|
for (var k = 0; k < columnCount; k++)
|
|
{
|
|
using var column = columns[k];
|
|
|
|
var fieldName = (string) column.Name;
|
|
var ordinalPosition = k;
|
|
var sortOrder = (SortOrderEnum) column.SortOrder;
|
|
var isDescending = sortOrder == SortOrderEnum.adSortDescending;
|
|
|
|
dataTable.Rows.Add(
|
|
tableName,
|
|
indexName,
|
|
ordinalPosition,
|
|
fieldName,
|
|
isDescending);
|
|
}
|
|
}
|
|
}
|
|
|
|
dataTable.AcceptChanges();
|
|
return dataTable;
|
|
}
|
|
|
|
public override DataTable GetRelations()
|
|
{
|
|
var dataTable = SchemaTables.GetRelationsDataTable();
|
|
|
|
using var tables = _catalog.Tables;
|
|
var tableCount = tables.Count;
|
|
|
|
for (var i = 0; i < tableCount; i++)
|
|
{
|
|
using var table = tables[i];
|
|
var referencingTableName = (string)table.Name;
|
|
|
|
using var keys = table.Keys;
|
|
var keyCount = (int) keys.Count;
|
|
|
|
for (var j = 0; j < keyCount; j++)
|
|
{
|
|
using var key = keys[j];
|
|
|
|
var relationName = (string) key.Name;
|
|
var principalTableName = (string) key.RelatedTable;
|
|
|
|
var relationType = !_naturalOnly ? "MANY" : null; // we don't know what kind of relationship this is
|
|
|
|
var updateRule = (RuleEnum) key.UpdateRule;
|
|
var onUpdate = updateRule switch
|
|
{
|
|
RuleEnum.adRINone => "NO ACTION",
|
|
RuleEnum.adRICascade => "CASCADE",
|
|
RuleEnum.adRISetNull => "SET NULL",
|
|
RuleEnum.adRISetDefault => "SET DEFAULT",
|
|
_ => "NO ACTION",
|
|
};
|
|
|
|
var deleteRule = (RuleEnum) key.DeleteRule;
|
|
var onDelete = deleteRule switch
|
|
{
|
|
RuleEnum.adRINone => "NO ACTION",
|
|
RuleEnum.adRICascade => "CASCADE",
|
|
RuleEnum.adRISetNull => "SET NULL",
|
|
RuleEnum.adRISetDefault => "SET DEFAULT",
|
|
_ => "NO ACTION",
|
|
};
|
|
|
|
var isEnforced = _naturalOnly ? null : (bool?) true;
|
|
var isInherited = _naturalOnly ? null : (bool?) true;
|
|
|
|
dataTable.Rows.Add(
|
|
relationName,
|
|
referencingTableName,
|
|
principalTableName,
|
|
relationType,
|
|
onUpdate,
|
|
onDelete,
|
|
isEnforced,
|
|
isInherited);
|
|
}
|
|
}
|
|
|
|
dataTable.AcceptChanges();
|
|
return dataTable;
|
|
}
|
|
|
|
public override DataTable GetRelationColumns()
|
|
{
|
|
var dataTable = SchemaTables.GetRelationColumnsDataTable();
|
|
|
|
using var tables = _catalog.Tables;
|
|
var tableCount = tables.Count;
|
|
|
|
for (var i = 0; i < tableCount; i++)
|
|
{
|
|
using var table = tables[i];
|
|
|
|
using var keys = table.Keys;
|
|
var keyCount = (int) keys.Count;
|
|
|
|
for (var j = 0; j < keyCount; j++)
|
|
{
|
|
using var key = keys[j];
|
|
|
|
var relationName = (string) key.Name;
|
|
var relationType = (KeyTypeEnum) key.Type;
|
|
|
|
if (relationType == KeyTypeEnum.adKeyForeign)
|
|
{
|
|
using var columns = key.Columns;
|
|
var columnCount = (int) columns.Count;
|
|
|
|
for (var k = 0; k < columnCount; k++)
|
|
{
|
|
using var column = columns[k];
|
|
|
|
var referencingColumnName = (string) column.Name;
|
|
var principalColumnName = (string) column.RelatedColumn;
|
|
|
|
dataTable.Rows.Add(
|
|
relationName,
|
|
referencingColumnName,
|
|
principalColumnName);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
dataTable.AcceptChanges();
|
|
return dataTable;
|
|
}
|
|
|
|
public override DataTable GetCheckConstraints()
|
|
{
|
|
var dataTable = SchemaTables.GetCheckConstraintsDataTable();
|
|
|
|
var checkConstraints = new Dictionary<string, string>();
|
|
|
|
using (var recordset = _connection.OpenSchema(SchemaEnum.adSchemaCheckConstraints))
|
|
{
|
|
using var fields = recordset.Fields;
|
|
using var constraintNameField = fields["CONSTRAINT_NAME"];
|
|
using var checkClauseField = fields["CHECK_CLAUSE"];
|
|
using var descriptionField = fields["DESCRIPTION"];
|
|
|
|
recordset.MoveFirst();
|
|
|
|
while (!recordset.EOF)
|
|
{
|
|
var constraintName = (string) constraintNameField.Value;
|
|
var checkClause = (string) checkClauseField.Value;
|
|
|
|
checkConstraints.Add(constraintName, checkClause);
|
|
recordset.MoveNext();
|
|
}
|
|
}
|
|
|
|
using (var recordset = _connection.OpenSchema(SchemaEnum.adSchemaTableConstraints))
|
|
{
|
|
using var fields = recordset.Fields;
|
|
using var tableNameField = fields["TABLE_NAME"];
|
|
using var constraintNameField = fields["CONSTRAINT_NAME"];
|
|
using var constraintTypeField = fields["CONSTRAINT_TYPE"];
|
|
|
|
recordset.MoveFirst();
|
|
|
|
while (!recordset.EOF)
|
|
{
|
|
var tableName = (string) tableNameField.Value;
|
|
var constraintName = (string) constraintNameField.Value;
|
|
//var constraintType = (string) constraintTypeField.Value;
|
|
//if (constraintType.StartsWith("CHECK", StringComparison.OrdinalIgnoreCase) &&
|
|
|
|
if (checkConstraints.TryGetValue(constraintName, out var checkClause))
|
|
{
|
|
dataTable.Rows.Add(
|
|
tableName,
|
|
constraintName,
|
|
checkClause);
|
|
}
|
|
|
|
recordset.MoveNext();
|
|
}
|
|
}
|
|
|
|
dataTable.AcceptChanges();
|
|
return dataTable;
|
|
}
|
|
|
|
protected static string GetDataTypeString(DataTypeEnum dataType, bool isIdentity = false)
|
|
{
|
|
switch (dataType)
|
|
{
|
|
case DataTypeEnum.adSmallInt:
|
|
return "smallint";
|
|
case DataTypeEnum.adInteger:
|
|
return isIdentity
|
|
? "counter"
|
|
: "integer";
|
|
case DataTypeEnum.adSingle:
|
|
return "single";
|
|
case DataTypeEnum.adDouble:
|
|
return "double";
|
|
case DataTypeEnum.adCurrency:
|
|
return "currency";
|
|
case DataTypeEnum.adDate:
|
|
return "datetime";
|
|
case DataTypeEnum.adBoolean:
|
|
return "bit";
|
|
case DataTypeEnum.adDecimal:
|
|
return "decimal";
|
|
case DataTypeEnum.adTinyInt:
|
|
return "smallint";
|
|
case DataTypeEnum.adUnsignedTinyInt:
|
|
return "byte";
|
|
case DataTypeEnum.adUnsignedSmallInt:
|
|
return "integer";
|
|
case DataTypeEnum.adUnsignedInt:
|
|
return null;
|
|
case DataTypeEnum.adBigInt:
|
|
return "integer";
|
|
case DataTypeEnum.adGUID:
|
|
return "guid";
|
|
case DataTypeEnum.adBinary:
|
|
return "binary";
|
|
case DataTypeEnum.adChar:
|
|
case DataTypeEnum.adWChar:
|
|
return "char";
|
|
case DataTypeEnum.adNumeric:
|
|
return "decimal";
|
|
case DataTypeEnum.adUserDefined:
|
|
return null;
|
|
case DataTypeEnum.adDBDate:
|
|
case DataTypeEnum.adDBTime:
|
|
return "datetime";
|
|
case DataTypeEnum.adDBTimeStamp:
|
|
return "timestamp";
|
|
case DataTypeEnum.adVarChar:
|
|
return "varchar";
|
|
case DataTypeEnum.adLongVarChar:
|
|
return "longchar";
|
|
case DataTypeEnum.adVarWChar:
|
|
return "varchar";
|
|
case DataTypeEnum.adLongVarWChar:
|
|
return "longchar";
|
|
case DataTypeEnum.adVarBinary:
|
|
return "varbinary";
|
|
case DataTypeEnum.adLongVarBinary:
|
|
return "longbinary";
|
|
case DataTypeEnum.adEmpty:
|
|
case DataTypeEnum.adBSTR:
|
|
case DataTypeEnum.adIDispatch:
|
|
case DataTypeEnum.adError:
|
|
case DataTypeEnum.adVariant:
|
|
case DataTypeEnum.adIUnknown:
|
|
case DataTypeEnum.adUnsignedBigInt:
|
|
case DataTypeEnum.adFileTime:
|
|
case DataTypeEnum.adChapter:
|
|
case DataTypeEnum.adPropVariant:
|
|
case DataTypeEnum.adVarNumeric:
|
|
default:
|
|
throw new ArgumentOutOfRangeException(nameof(dataType), $"Could not map a data type of '{Enum.GetName(typeof(DataTypeEnum), dataType)}'.");
|
|
}
|
|
}
|
|
|
|
protected static int? GetMaxLength(DataTypeEnum dataType, int size)
|
|
=> dataType switch
|
|
{
|
|
DataTypeEnum.adBinary => size,
|
|
DataTypeEnum.adChar => size,
|
|
DataTypeEnum.adWChar => size,
|
|
DataTypeEnum.adVarBinary => size,
|
|
DataTypeEnum.adVarChar => size,
|
|
DataTypeEnum.adVarWChar => size,
|
|
DataTypeEnum.adLongVarBinary => null,
|
|
DataTypeEnum.adLongVarChar => null,
|
|
DataTypeEnum.adLongVarWChar => null,
|
|
_ => null
|
|
};
|
|
|
|
private static T GetPropertyValue<T>(dynamic properties, string name, T defaultValue = default)
|
|
{
|
|
try
|
|
{
|
|
using var property = properties[name];
|
|
return (T)property.Value;
|
|
}
|
|
catch
|
|
{
|
|
#if DEBUG
|
|
var propertyMap = GetProperties(properties);
|
|
#endif
|
|
return defaultValue;
|
|
}
|
|
}
|
|
|
|
protected static Dictionary<string, object> GetProperties(dynamic properties)
|
|
{
|
|
var propertyMap = new Dictionary<string, object>();
|
|
var propertyCount = properties.Count;
|
|
for (var k = 0; k < propertyCount; k++)
|
|
{
|
|
string key;
|
|
object value;
|
|
try
|
|
{
|
|
using var p = properties[k];
|
|
key = (string)p.Name;
|
|
value = p.Value;
|
|
propertyMap.Add(key, value);
|
|
}
|
|
catch
|
|
{
|
|
// ignored
|
|
}
|
|
}
|
|
|
|
return propertyMap;
|
|
}
|
|
|
|
public override void Dispose()
|
|
{
|
|
_connection.Dispose();
|
|
_catalog.Dispose();
|
|
}
|
|
|
|
protected enum RuleEnum
|
|
{
|
|
adRINone = 0,
|
|
adRICascade = 1,
|
|
adRISetNull = 2,
|
|
adRISetDefault = 3,
|
|
}
|
|
|
|
protected enum SortOrderEnum
|
|
{
|
|
adSortAscending = 1,
|
|
adSortDescending = 2,
|
|
}
|
|
|
|
protected enum KeyTypeEnum
|
|
{
|
|
adKeyPrimary = 1,
|
|
adKeyForeign = 2,
|
|
adKeyUnique = 3,
|
|
}
|
|
|
|
protected enum AllowNullsEnum
|
|
{
|
|
adIndexNullsAllow = 0,
|
|
adIndexNullsDisallow = 1,
|
|
adIndexNullsIgnore = 2,
|
|
adIndexNullsIgnoreAny = 4,
|
|
}
|
|
|
|
[Flags]
|
|
protected enum ColumnAttributesEnum
|
|
{
|
|
adColFixed = 1,
|
|
adColNullable = 2,
|
|
}
|
|
|
|
protected enum DataTypeEnum
|
|
{
|
|
adEmpty = 0,
|
|
adSmallInt = 2,
|
|
adInteger = 3,
|
|
adSingle = 4,
|
|
adDouble = 5,
|
|
adCurrency = 6,
|
|
adDate = 7,
|
|
adBSTR = 8,
|
|
adIDispatch = 9,
|
|
adError = 10,
|
|
adBoolean = 11,
|
|
adVariant = 12,
|
|
adIUnknown = 13,
|
|
adDecimal = 14,
|
|
adTinyInt = 16,
|
|
adUnsignedTinyInt = 17,
|
|
adUnsignedSmallInt = 18,
|
|
adUnsignedInt = 19,
|
|
adBigInt = 20,
|
|
adUnsignedBigInt = 21,
|
|
adFileTime = 64,
|
|
adGUID = 72,
|
|
adBinary = 128,
|
|
adChar = 129,
|
|
adWChar = 130,
|
|
adNumeric = 131,
|
|
adUserDefined = 132,
|
|
adDBDate = 133,
|
|
adDBTime = 134,
|
|
adDBTimeStamp = 135,
|
|
adChapter = 136,
|
|
adPropVariant = 138,
|
|
adVarNumeric = 139,
|
|
adVarChar = 200,
|
|
adLongVarChar = 201,
|
|
adVarWChar = 202,
|
|
adLongVarWChar = 203,
|
|
adVarBinary = 204,
|
|
adLongVarBinary = 205,
|
|
}
|
|
|
|
// https://docs.microsoft.com/en-us/office/client-developer/access/desktop-database-reference/schemaenum
|
|
protected enum SchemaEnum
|
|
{
|
|
adSchemaProviderSpecific = -1,
|
|
adSchemaAsserts = 0,
|
|
adSchemaCatalogs = 1,
|
|
adSchemaCharacterSets = 2,
|
|
adSchemaCollations = 3,
|
|
adSchemaColumns = 4,
|
|
adSchemaCheckConstraints = 5,
|
|
adSchemaConstraintColumnUsage = 6,
|
|
adSchemaConstraintTableUsage = 7,
|
|
adSchemaKeyColumnUsage = 8,
|
|
AdSchemaReferentialConstraints = 9,
|
|
adSchemaTableConstraints = 10,
|
|
adSchemaColumnsDomainUsage = 11,
|
|
adSchemaIndexes = 12,
|
|
adSchemaColumnPrivileges = 13,
|
|
adSchemaTablePrivileges = 14,
|
|
adSchemaUsagePrivileges = 15,
|
|
adSchemaProcedures = 16,
|
|
adSchemaSchemata = 17,
|
|
adSchemaSQLLanguages = 18,
|
|
adSchemaStatistics = 19,
|
|
adSchemaTables = 20,
|
|
adSchemaTranslations = 21,
|
|
adSchemaProviderTypes = 22,
|
|
adSchemaViews = 23,
|
|
adSchemaViewColumnUsage = 24,
|
|
adSchemaViewTableUsage = 25,
|
|
adSchemaProcedureParameters = 26,
|
|
adSchemaForeignKeys = 27,
|
|
adSchemaPrimaryKeys = 28,
|
|
adSchemaProcedureColumns = 29,
|
|
adSchemaDBInfoKeywords = 30,
|
|
adSchemaDBInfoLiterals = 31,
|
|
adSchemaCubes = 32,
|
|
adSchemaDimensions = 33,
|
|
adSchemaHierarchies = 34,
|
|
adSchemaLevels = 35,
|
|
adSchemaMeasures = 36,
|
|
adSchemaProperties = 37,
|
|
adSchemaMembers = 38,
|
|
adSchemaTrustees = 39,
|
|
}
|
|
|
|
[Flags]
|
|
protected enum CommandTypeEnum
|
|
{
|
|
adCmdUnspecified = -1,
|
|
adCmdText = 0x00000001,
|
|
adCmdTable = 0x00000002,
|
|
adCmdStoredProc = 0x00000004,
|
|
adCmdUnknown = 0x00000008,
|
|
adCmdFile = 0x00000100,
|
|
adCmdTableDirect = 0x00000200,
|
|
}
|
|
|
|
[Flags]
|
|
protected enum ExecuteOptionEnum
|
|
{
|
|
adOptionUnspecified = -1,
|
|
adAsyncExecute = 0x00000010,
|
|
adAsyncFetch = 0x00000020,
|
|
adAsyncFetchNonBlocking = 0x00000040,
|
|
adExecuteNoRecords = 0x00000080,
|
|
adExecuteStream = 0x00000400,
|
|
//adExecuteRecord = ???,
|
|
}
|
|
|
|
[Flags]
|
|
protected enum FieldAttributeEnum
|
|
{
|
|
adFldCacheDeferred = 0x1000,
|
|
adFldFixed = 0x10,
|
|
adFldIsChapter = 0x2000,
|
|
adFldIsCollection = 0x40000,
|
|
adFldIsDefaultStream = 0x20000,
|
|
adFldIsNullable = 0x20,
|
|
adFldIsRowURL = 0x10000,
|
|
adFldLong = 0x80,
|
|
adFldMayBeNull = 0x40,
|
|
adFldMayDefer = 0x2,
|
|
adFldNegativeScale = 0x4000,
|
|
adFldRowID = 0x100,
|
|
adFldRowVersion = 0x200,
|
|
adFldUnknownUpdatable = 0x8,
|
|
adFldUnspecified = -1,
|
|
adFldUpdatable = 0x4,
|
|
}
|
|
}
|
|
} |