使用EF Core 2.1检查表是否存在

5
在Entity Framework中,可以通过以下方式检查表的存在性:
bool exists = context.Database
                 .SqlQuery<int?>(@"
                     SELECT 1 FROM sys.tables AS T
                     INNER JOIN sys.schemas AS S ON T.schema_id = S.schema_id
                     WHERE S.Name = 'SchemaName' AND T.Name = 'TableName'")
                 .SingleOrDefault() != null;

我正在使用 EF Core 2.1,但是方法 SqlQuery 并不存在。
正确的检查表是否存在的方法是什么?最好不要尝试访问该表,并假设如果抛出异常则表不存在。 编辑:我的最终实现
public bool TableExists(string tableName)
{
    return TableExists("dbo", tableName);
}

public bool TableExists(string schema, string tableName)
{
    var connection = Context.Database.GetDbConnection();

    if (connection.State.Equals(ConnectionState.Closed))
        connection.Open();

    using (var command = connection.CreateCommand())
    {
        command.CommandText = @"
            SELECT 1 FROM INFORMATION_SCHEMA.TABLES 
            WHERE TABLE_SCHEMA = @Schema
            AND TABLE_NAME = @TableName";

        var schemaParam = command.CreateParameter();
        schemaParam.ParameterName = "@Schema";
        schemaParam.Value = schema;
        command.Parameters.Add(schemaParam);

        var tableNameParam = command.CreateParameter();
        tableNameParam.ParameterName = "@TableName";
        tableNameParam.Value = tableName;
        command.Parameters.Add(tableNameParam);

        return command.ExecuteScalar() != null;
    }
}

1
你能详细说明一下“不合法”的含义吗? - jazb
你尝试过使用 FromSql 吗? - Kien Chu
这个问题是关于“开放查询”一般性的,还是严格限定于表存在检查? - user2864740
@JohnB 这个方法在 EF Core 中不存在。 - AndreFeijo
1个回答

7

有一个ExecuteSqlCommand方法与之对应。

context.Database.ExecuteSqlCommand("...")

然而,它仅返回一个整数表示受影响的行数。如果执行的是SELECT语句,则不会有行受到影响,因此它并不能满足您的需求。
还有一个FromSql方法,但它只适用于表而不是数据库级别:
context.TableName.FromSql("SELECT ...")

针对您所做的事情,更好的选择是从EF获取DbConnection,并创建自己的DbCommand。这将返回您所期望的内容:

var conn = context.Database.GetDbConnection();
if (conn.State.Equals(ConnectionState.Closed)) await conn.OpenAsync();
using (var command = conn.CreateCommand()) {
    command.CommandText = @"
    SELECT 1 FROM sys.tables AS T
        INNER JOIN sys.schemas AS S ON T.schema_id = S.schema_id
    WHERE S.Name = 'SchemaName' AND T.Name = 'TableName'";
    var exists = await command.ExecuteScalarAsync() != null;
}

这里有个警告:不要在using语句中放置从GetDbConnection()获得的DbConnection,也不要在使用完后关闭它。这个连接会被用于该DbContext实例的生命周期。因此,如果你关闭它,EF之后的任何请求都会失败。还有可能在你的代码之前已经打开了它,这就是我放置测试来查看是否已关闭再调用OpenAsync()的原因。

如何使用FromSql检查表是否存在?据我所知,它需要一个实体进行映射,那么我是否需要为Information Schema构建一个Dto呢? - AndreFeijo
您需要已知数据表才能使用FromSql,因此我认为您无法使用它来确定一个表是否存在。 - Gabriel Luci
1
既然我回到了办公室,我可以做一些测试。我找到了另一种方法。我已经更新了我的答案。 - Gabriel Luci
1
谢谢Gabriel,我也已经编辑了我的问题并附上了最终实现。 - AndreFeijo
注意:关闭连接是可以的(它将被重新打开),但是释放连接(根据使用)不行,因为未来的打开调用将失败。实际上,确保连接已关闭可能更好,以立即将连接返回到池中。 - user2864740

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接