有没有办法使用ADO.NET GetSchema
方法确定列是否为主键?
这是我到目前为止所得到的:
public IEnumerable<DbColumnInfo> GetColumns(string providerName, string connectionString, string tableName)
{
DbProviderFactory factory = DbProviderFactories.GetFactory(providerName);
using (DbConnection connection = factory.CreateConnection())
using (DbCommand command = factory.CreateCommand())
{
connection.ConnectionString = connectionString;
connection.Open();
command.Connection = connection;
var columns = connection.GetSchema("Columns", tableName.Split('.'));
foreach (DataRow row in columns.Rows)
{
yield return new DbColumnInfo()
{
Name = row.Field<string>(3),
OrdinalPosition = row.Field<short>(4),
DataType = this.FormatDataType(row),
IsNullable = string.Equals(row.Field<string>(6), "yes", StringComparison.InvariantCultureIgnoreCase),
IsPrimaryKey = // ... ?
};
}
}
}
答案 0 :(得分:4)
我担心你无法通过connection.GetSchema()...
来确定但作为一种解决方法,您可以尝试使用dataadapter,如果它适合您:
var da = factory.CreateDataAdapter();
command.CommandText = "select * from Employees";
da.SelectCommand = command;
da.MissingSchemaAction = MissingSchemaAction.AddWithKey;
var dtab = new DataTable();
da.FillSchema(dtab, SchemaType.Source);
foreach (DataColumn col in dtab.Columns)
{
string name = col.ColumnName;
bool isNull = col.AllowDBNull;
bool isPrimary = dtab.PrimaryKey.Contains(col);
}
答案 1 :(得分:3)
自 qes 询问后,我正在使用的内容(在 Laszlo 提供他的工作解决方案之后重构代码的几个版本之后)并不像我希望的那样优雅因为,但它更有效,满足了我的需求。基本上我提供了一个DbInfoProvider
类,旨在为给定的提供者生成DbTableInfo
和DbColumnInfo
。 SqlDbInfoProvider
使用它来获取SQL Server的列信息:
public IEnumerable<DbColumnInfo> GetColumns(string connectionString, DbTableInfo table)
{
DbProviderFactory factory = DbProviderFactories.GetFactory(this.providerName);
using (DbConnection connection = factory.CreateConnection())
using (DbCommand command = factory.CreateCommand())
{
connection.ConnectionString = connectionString;
connection.Open();
command.Connection = connection;
command.CommandText = ColumnInfoQuery;
command.CommandType = CommandType.Text;
var tableSchema = factory.CreateParameter();
tableSchema.ParameterName = "@tableSchema";
tableSchema.DbType = DbType.String;
tableSchema.Value = table.Schema;
command.Parameters.Add(tableSchema);
var tableName = factory.CreateParameter();
tableName.ParameterName = "@tableName";
tableName.DbType = DbType.String;
tableName.Value = table.Name;
command.Parameters.Add(tableName);
var dataTable = new DataTable();
using (var reader = command.ExecuteReader())
{
while (reader.Read())
{
yield return new DbColumnInfo()
{
Name = reader.GetString(0),
OrdinalPosition = reader.GetInt32(1),
DataType = reader.GetString(2),
IsNullable = reader.GetBoolean(3),
IsPrimaryKey = reader.GetBoolean(4),
IsForeignKey = reader.GetBoolean(5),
IsUnique = reader.GetBoolean(6),
};
}
}
}
}
其中ColumnInfoQuery
是这样的静态字符串:
SELECT c.[column_name]
, CAST(c.[ordinal_position] AS int) [ordinal_position]
, CASE WHEN c.[data_type] IN ( 'bit', 'date', 'datetime', 'smalldatetime', 'int', 'bigint', 'smallint', 'tinyint', 'real', 'money', 'smallmoney', 'image', 'text', 'ntext' ) THEN c.[data_type]
WHEN c.[character_maximum_length] IS NOT NULL
THEN c.[data_type] + '(' + CAST(c.[character_maximum_length] AS VARCHAR(30)) + ')'
WHEN c.[datetime_precision] IS NOT NULL
THEN c.[data_type] + '(' + CAST(c.[datetime_precision] AS VARCHAR(30)) + ')'
WHEN c.[numeric_scale] IS NOT NULL
THEN c.[data_type] + '(' + CAST(c.[numeric_precision] AS VARCHAR(30)) + ',' + CAST(c.[numeric_scale] AS VARCHAR(30)) + ')'
WHEN c.[numeric_precision] IS NOT NULL
THEN c.[data_type] + '(' + CAST(c.[numeric_precision] AS VARCHAR(30)) + ')'
ELSE c.[data_type]
END [data_type]
, CAST(MAX(CASE c.[is_nullable] WHEN 'YES' THEN 1 ELSE 0 END) AS bit) [is_nullable]
, CAST(MAX(CASE WHEN pk.[constraint_type] = 'PRIMARY KEY' THEN 1 ELSE 0 END) AS bit) [is_primary_key]
, CAST(MAX(CASE WHEN pk.[constraint_type] = 'FOREIGN KEY' THEN 1 ELSE 0 END) AS bit) [is_foreign_key]
, CAST(MAX(CASE WHEN pk.[constraint_type] = 'FOREIGN KEY' THEN 0 ELSE 1 END) AS bit) [is_unique]
FROM information_schema.columns c
LEFT JOIN information_schema.constraint_column_usage ccu
ON c.[column_name] = ccu.[column_name]
AND c.[table_name] = ccu.[table_name]
AND c.[table_schema] = ccu.[table_schema]
AND c.[table_catalog] = ccu.[table_catalog]
LEFT JOIN information_schema.table_constraints pk
ON pk.[constraint_name] = ccu.[constraint_name]
AND pk.[table_name] = ccu.[table_name]
AND pk.[constraint_schema] = ccu.[table_schema]
AND pk.[constraint_catalog] = ccu.[table_catalog]
AND pk.[constraint_type] IN ( 'PRIMARY KEY', 'FOREIGN KEY', 'UNIQUE' )
WHERE c.[table_schema] = @tableSchema
AND c.[table_name] = @tableName
GROUP BY c.[table_schema], c.[table_name], c.[column_name], c.[ordinal_position]
, c.[data_type], c.[character_maximum_length], c.[datetime_precision]
, c.[numeric_precision], c.[numeric_scale], c.[is_nullable]
答案 2 :(得分:2)
是, 您可以通过请求索引的架构来确定哪些列是主键。然后在索引中搜索特定的列/表
DataTable indexes = conn.GetSchema("Indexes");
List<string> PrimaryKeys = new List<string>();
foreach (DataRow row in indexes.Rows)
if (Convert.ToBoolean(row["PRIMARY_KEY"]))
PrimaryKeys.Add(row["TABLE_NAME"] + "." + row["COLUMN_NAME"]);
PrimaryKeys将包含数据库中的主键列表。只需检查您的[table]。[column]是否在此列表中。
答案 3 :(得分:0)
这适用于SQLITE和其他可能的数据库: -
string[] restrictions = new string[] { null, null, strTable };
DataTable tableInfo = Connection.GetSchema("IndexColumns", restrictions);
if (tableInfo == null)
throw new Exception("TableInfo null Error");
foreach (DataRow test in tableInfo.Rows)
{
Console.WriteLine(test["column_name"]);
}
答案 4 :(得分:0)
至少在Oracle ODP.Net提供程序中,GetSchema()有一个“PrimaryKeys”选项。我建议您使用没有参数的GetSchema()来查看为您的实现定义的其他调用。此代码检索GetSchema()元定义,该元定义描述了Oracle提供程序的其他已实现的GetSchema()调用。
private void GetSchemaMetaInfo(DbConnection connection)
{
var metaDataCollections = connection.GetSchema("MetaDataCollections");
var dataSourceInformation = connection.GetSchema("DataSourceInformation");
var dataTypes = connection.GetSchema("DataTypes");
var restrictions = connection.GetSchema("Restrictions");
var reservedWords = connection.GetSchema("ReservedWords");
}
答案 5 :(得分:0)
可以使用PrimaryKeys集合来确定主键 为您的特定表,然后检索相应的索引名称,然后从IndexColumns集合中获取构成索引和主键的列。
String pkIndxNm = null;
List<String> lstPkColNms = new List<String>();
DataTable dt = dbConn.GetSchema("PrimaryKeys", new[] { owner, tblNm });
if (dt.Rows.Count == 1) {
DataRow pkRow = dt.Rows[0];
pkIndxNm = pkRow["INDEX_NAME"] as String;
// Now use the IndexColumns collection to pick up the names of the
// columns which constitute the primary key.
//
dt = dbConn.GetSchema("IndexColumns", new[] { owner, pkIndxNm });
foreach (DataRow icRow in dt.Rows) {
String colNm = icRow["COLUMN_NAME"] as String;
lstPkColNms.Add(colNm);
}
}
这使用System.Data.Common.DbConnection对Oracle数据库起作用 具有基础System.Data.OracleClient连接的类。
对于其他数据库,您可能需要检查Restrictions集合 什么你可以作为参数传递。 GetSchema限制的文档 看起来很无用。我发现只是转储了DataTable 限制集合是了解每个集合的最佳方式 最好被查询:
DataTable dt = dbConn.GetSchema("Restrictions");
AppLog.Log.Info("CollectionName | RestrictionName | ParameterName | " +
"RestrictionDefault | RestrictionNumber");
AppLog.Log.Info(" ");
foreach (DataRow r in dt.Rows) {
String s = r["CollectionName"] as String;
s += " | " + r["RestrictionName"] as String;
s += " | " + r["ParameterName"] as String;
s += " | " + r["RestrictionDefault"] as String;
s += " | " + r["RestrictionNumber"].ToString();
AppLog.Log.Info(s);
}
这是我得到的,从中我知道传递所有者和indexName 数组的第一个和第二个位置将其约束到正确的索引。
用户|用户名| NAME | USERNAME | 1
表|所有者|所有者|所有者| 1
表|表| TABLENAME | TABLE_NAME | 2
列|所有者|所有者|所有者| 1
列|表| TABLENAME | TABLE_NAME | 2
列|专栏| COLUMNNAME | COLUMN_NAME | 3
观看次数|所有者|所有者|所有者| 1
观看次数|查看| VIEWNAME | VIEW_NAME | 2
同义词|所有者|所有者|所有者| 1
同义词|同义词| SYNONYMNAME | SYNONYM_NAME | 2
序列|所有者|所有者| SEQUENCE_OWNER | 1
序列|序列| SEQUENCE | SEQUENCE_NAME | 2
ProcedureParameters |所有者|所有者|所有者| 1
ProcedureParameters | ObjectName | OBJECTNAME | OBJECT_NAME | 2
功能|所有者|所有者|所有者| 1
功能|名称| NAME | OBJECT_NAME | 2
IndexColumns |所有者|所有者| INDEX_OWNER | 1
IndexColumns |名称| NAME | INDEX_NAME | 2
IndexColumns | TableOwner | TABLEOWNER | TABLE_OWNER | 3
IndexColumns | TableName | TABLENAME | TABLE_NAME | 4
IndexColumns |专栏| COLUMNNAME | COLUMN_NAME | 5
索引|所有者|所有者|所有者| 1
索引|名称| NAME | INDEX_NAME | 2
索引| TableOwner | TABLEOWNER | TABLE_OWNER | 3
索引| TableName | TABLENAME | TABLE_NAME | 4
套餐|所有者|所有者|所有者| 1
套餐|名称| PACKAGENAME | OBJECT_NAME | 2
PackageBodies |所有者|所有者|所有者| 1
PackageBodies |名称| NAME | OBJECT_NAME | 2
参数|所有者|所有者|所有者| 1
参数| PackageName | PACKAGENAME | PACKAGE_NAME | 2
参数| ObjectName | OBJECTNAME | OBJECT_NAME | 3
参数| ArgumentName | ARGUMENTNAME | ARGUMENT_NAME | 4
程序|所有者|所有者|所有者| 1
程序|名称| NAME | OBJECT_NAME | 2
UniqueKeys |所有者|所有者|所有者| 1
UniqueKeys | Table_Name | TABLENAME | TABLE_NAME | 2
UniqueKeys | Constraint_Name | CONSTRAINTNAME | CONSTRAINT_NAME | 3
PrimaryKeys |所有者|所有者|所有者| 1
PrimaryKeys | Table_Name | TABLENAME | TABLE_NAME | 2
PrimaryKeys | Constraint_Name | CONSTRAINTNAME | CONSTRAINT_NAME | 3
ForeignKeys | Foreign_Key_Owner |所有者| FKCON.OWNER | 1
ForeignKeys | Foreign_Key_Table_Name | TABLENAME | FKCON.TABLE_NAME | 2
ForeignKeys | Foreign_Key_Constraint_Name | CONSTRAINTNAME | FKCON.CONSTRAINT_NAME | 3
ForeignKeyColumns |所有者|所有者| FKCOLS.OWNER | 1
ForeignKeyColumns | Table_Name | TABLENAME | FKCOLS.TABLE_NAME | 2
ForeignKeyColumns | Constraint_Name | CONSTRAINTNAME | FKCOLS.CONSTRAINT_NAME | 3