连续两次调用 Database.GetDbConnection() 会导致 System.NullReferenceException

Calling Database.GetDbConnection() twice in a row causes a System.NullReferenceException

public async Task<IEnumerable<Bar>> GetFoo()
{
    using var connection = this.Database.GetDbConnection();         
    var bars = new List<Bar>();
    try{

        var cmd = new SqlCommand
        {
            CommandText = "select * from GetFoo()",
            CommandType = System.Data.CommandType.Text,
            Connection = connection as SqlConnection
        };
       
    
        await connection.OpenAsync();
        using var reader = await cmd.ExecuteReaderAsync();           
        while (await reader.ReadAsync())
        {
            var bar = new Bar
            {
                Foo = (string) reader["FOO"],
            };
            bars.Add(bar);
        }
    }
    finally
    {
        //await connection.CloseAsync();
        //await connection.DisposeAsync();
    }

    return bars;
}

当运行这个方法两次我得到:

System.NullReferenceException:'Object reference not set to an instance of an object.' 在行 await connection.OpenAsync();

这是我所知道的:

If EF creates the DbConnection object, then EF will ensure it is disposed when the DbContext is disposed. On the other hand, if some other code creates the DbConnection object and passes it to EF, then it is the responsibility of the other code to also dispose the connection appropriately.

那我做错了什么?有什么办法可以克服这个问题吗? this.Datbase.GetDbConnection() 可以在同一实例中调用两次吗?

如你所说:

If EF creates the DbConnection object, then EF will ensure it is disposed when the DbContext is disposed.

因此,当 EF 创建连接时,而不是显式处理从 this.Database.GetDbConnection() 获得的连接,您可以只接受它并 open/close 它:

public async Task<IEnumerable<Bar>> GetFoo()
{
    var connection = this.Database.GetDbConnection();

    using var cmd = new SqlCommand
    {
        CommandText = "select * from GetFoo()",
        CommandType = System.Data.CommandType.Text,
        Connection = connection
    };

    var bars = new List<Bar>();

    try
    {
        await connection.OpenAsync();
        using var reader = await cmd.ExecuteReaderAsync();

        while (await reader.ReadAsync())
        {
            var bar = new Bar
            {
                Foo = (string)reader["FOO"],
            };
            bars.Add(bar);
        }

        return bars;
    }
    finally
    {
        await connection.CloseAsync();
    }
}

这样你就不会得到 NullReferenceException 并且你的连接会被正确处理,因为每次你调用 connection.CloseAwait(),ADO.Net 都会释放到连接池的连接,或者关闭连接如果连接池已禁用 (reference)。 稍后 EF 将在处理 DbContext 时处理连接。

此外,如果您的 sql 查询非常标准,您还可以将 SqlCommand 替换为 DbCommand 并使您的代码 provider-independent:

    using var cmd = connection.CreateCommand();
    cmd.CommandText = "select * from GetFoo()";
    //you can remove this line, it's by default set to Text
    cmd.CommandType = System.Data.CommandType.Text;