C# 如何在ADO.NET中获取主键列?

C# 如何在ADO.NET中获取主键列?,c#,ado.net,C#,Ado.net,我有一张桌子: CREATE TABLE [dbo].[DeliveryData]( [DeliveryId] [int] IDENTITY(1,1) NOT NULL, ... CONSTRAINT [PK_DeliveryData] PRIMARY KEY CLUSTERED ( [DeliveryId] ASC )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, AL

我有一张桌子:

CREATE TABLE [dbo].[DeliveryData](
    [DeliveryId] [int] IDENTITY(1,1) NOT NULL,
   ...
CONSTRAINT [PK_DeliveryData] PRIMARY KEY CLUSTERED 
(
    [DeliveryId] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
和代码:

public void GetPrimaryKeyColumns(SqlConnection conn) {
    SqlCommand cmd = conn.CreateCommand();
    cmd.CommandText = "select * from [dbo].[DeliveryData]";
    SqlDataReader reader = cmd.ExecuteReader(CommandBehavior.SchemaOnly);
    DataTable schema = reader.GetSchemaTable();
    DataColumn[] columns = schema.PrimaryKey;
    ...
}
cmd、reader和schema看起来都不错,但列最终是一个零长度数组。它不应该包含DeliveryId吗?如何获取主列DeliveryId

谢谢你的帮助


Blake

模式表不是DeliveryData表。您必须检查IsKey列为true的模式表,然后获取ColumnName字段。然后,您可以使用它查找常规数据表上的实际列

更新

GetSchemaTable返回元数据信息的数据表,您可以在文档中看到:

如果要运行查询,则返回的数据表中的行数与查询中的列数相同。下面是我从数据库中的测试表中获得的模式表的部分屏幕截图。请注意,现在每一列都是一行,IsKey字段将告诉您该列是否为键列:

如果要在DataTable上使用PrimaryKey属性,请不要使用GetSchemaTable,只需使用SqlDataAdapter填充常规DataTable即可

更新2

使用CommandBehavior.KeyInfo而不是CommandBehavior.SchemaOnly

使用SMO


MSSQL不会在所有情况下使用GetSchemaTable返回正确的主键信息。不足为奇。例如,几乎所有数据库供应商都比MS更好地支持ODBC。但是,以下查询确实有效:

SELECT COLUMN_NAME
FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS as tc
LEFT JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE as  kcu
    ON kcu.CONSTRAINT_CATALOG = tc.CONSTRAINT_CATALOG
    AND kcu.CONSTRAINT_SCHEMA = tc.CONSTRAINT_SCHEMA
    AND kcu.CONSTRAINT_NAME = tc.CONSTRAINT_NAME
    AND kcu.TABLE_CATALOG = tc.TABLE_CATALOG
    AND kcu.TABLE_SCHEMA = tc.TABLE_SCHEMA
    AND kcu.TABLE_NAME = tc.TABLE_NAME
WHERE tc.CONSTRAINT_TYPE ='PRIMARY KEY'
    AND tc.TABLE_SCHEMA = 'dbo'
    AND tc.TABLE_NAME = 'DeliveryData'
ORDER BY ORDINAL_POSITION;

这是一个完整的解决方案:

public List<string> GetPrimaryKeyColumns(DbConnection conn, string schema, string table) {
    DbCommand cmd = conn.CreateCommand();

    DbParameter p = cmd.CreateParameter();
    p.ParameterName = "@schema";
    p.Value = schema;
    p.DbType = DbType.String; 
    p.Direction = ParameterDirection.Input;
    cmd.Parameters.Add(p);

    p = cmd.CreateParameter();
    p.ParameterName = "@table";
    p.Value = table;
    p.DbType = DbType.String;
    p.Direction = ParameterDirection.Input;
    cmd.Parameters.Add(p);

    cmd.CommandText = @"SELECT kcu.COLUMN_NAME
                        FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS as tc
                        LEFT JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE as  kcu
                            ON kcu.CONSTRAINT_CATALOG = tc.CONSTRAINT_CATALOG
                               AND kcu.CONSTRAINT_SCHEMA = tc.CONSTRAINT_SCHEMA
                               AND kcu.CONSTRAINT_NAME = tc.CONSTRAINT_NAME
                               -- AND kcu.TABLE_CATALOG = tc.TABLE_CATALOG  doesn't work on MySQL
                               AND kcu.TABLE_SCHEMA = tc.TABLE_SCHEMA
                               AND kcu.TABLE_NAME = tc.TABLE_NAME
                        WHERE tc.CONSTRAINT_TYPE ='PRIMARY KEY'
                              AND tc.TABLE_SCHEMA = @schema
                              AND tc.TABLE_NAME = @table
                        ORDER BY ORDINAL_POSITION";

    DbDataReader reader = cmd.ExecuteReader(CommandBehavior.KeyInfo);

    List<string> res = new List<string>();
    while (reader.Read()) {
        var str = reader[0];
        if (str != System.DBNull.Value)
            res.Add((string) str);
    }
    reader.Dispose();
    cmd.Dispose();
    return res;
}

您从未打开连接,因此cmd.ExecuteReader应该引发异常。我怀疑你有一个隐藏例外的陷阱。我感谢你的帮助,但我还是迷路了。cmd.CommandText指定返回内容的布局。读卡器应该用于DeliveryData。我并不是在要求完整的数据库模式。此外,查找具有pri集的列对复合主键也没有帮助。你能再唠叨一点吗?谢谢您提供的链接状态为返回一个DataTable,该DataTable描述SqlDataReader的列元数据。。我的读卡器位于DeliveryData表上,因此我的读卡器上的GetSchemaTable应该提供我的读卡器的模式。此外,IsKey字段不适用于复合键。你能修改我最初提供的代码吗?谢谢它返回结果集的模式。放置断点并使用DataTable Visualizer查看得到的表。除了不同的列名,它看起来和我的一样。我觉得您根本不想使用GetSchemaTable,我想您误解了它是什么。但是,对于参与主键的所有列,IsKey都将为true,因此如果需要,您仍然可以使用它。但有两个问题。首先,根据我向您展示的表定义,没有一个IsKey字段是正确的。其次,我不能使用这个fopr复合键,因为即使我知道它由哪些列组成,我也不知道索引的列顺序。我认为您希望使用SqlDataAdapter用命令填充数据表,并对该表使用PrimaryKey属性,而不是使用模式表。但是,这两种方法都没有给我测试表的主键。似乎不可靠。也许你应该使用来检查表元数据。你看到我的更新了吗?如果你使用KeyInfo命令行为的话。验证了它的工作原理,正如MS所记录的。虽然您的更新确实让我了解了您的起点,但它仍然不适用于复合密钥。另外,我打赌如果我在三个不同的列上放置一个单独的索引,这三个列中的每一个都会显示为IsKey true。一点帮助都没有。很高兴你解决了你的问题,但这不是你问的问题。也许你可以加上一些理由,这样谷歌就不会因为错误的原因登陆这里。我非常感谢你的帮助。这让我一直在寻找解决方案。我最初的问题是:如何在ADO.NET中获取主键列?答案是:您不能通过ADO.NET模式API。但是,您可以使用我随普通ADO.NET查询API提供的ANSI 92标准查询。
public List<string> GetPrimaryKeyColumns(DbConnection conn, string schema, string table) {
    DbCommand cmd = conn.CreateCommand();

    DbParameter p = cmd.CreateParameter();
    p.ParameterName = "@schema";
    p.Value = schema;
    p.DbType = DbType.String; 
    p.Direction = ParameterDirection.Input;
    cmd.Parameters.Add(p);

    p = cmd.CreateParameter();
    p.ParameterName = "@table";
    p.Value = table;
    p.DbType = DbType.String;
    p.Direction = ParameterDirection.Input;
    cmd.Parameters.Add(p);

    cmd.CommandText = @"SELECT kcu.COLUMN_NAME
                        FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS as tc
                        LEFT JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE as  kcu
                            ON kcu.CONSTRAINT_CATALOG = tc.CONSTRAINT_CATALOG
                               AND kcu.CONSTRAINT_SCHEMA = tc.CONSTRAINT_SCHEMA
                               AND kcu.CONSTRAINT_NAME = tc.CONSTRAINT_NAME
                               -- AND kcu.TABLE_CATALOG = tc.TABLE_CATALOG  doesn't work on MySQL
                               AND kcu.TABLE_SCHEMA = tc.TABLE_SCHEMA
                               AND kcu.TABLE_NAME = tc.TABLE_NAME
                        WHERE tc.CONSTRAINT_TYPE ='PRIMARY KEY'
                              AND tc.TABLE_SCHEMA = @schema
                              AND tc.TABLE_NAME = @table
                        ORDER BY ORDINAL_POSITION";

    DbDataReader reader = cmd.ExecuteReader(CommandBehavior.KeyInfo);

    List<string> res = new List<string>();
    while (reader.Read()) {
        var str = reader[0];
        if (str != System.DBNull.Value)
            res.Add((string) str);
    }
    reader.Dispose();
    cmd.Dispose();
    return res;
}