Search code examples
c#sql-server.net-coredapper

System.InvalidOperationException: Invalid attempt to call FieldCount when reader is closed


  1. I would have expected the queryOutput to be materialized?
  2. Why is there the invalid attempt to call fieldcount when it alreadyhas the IEnumerable?

/// <summary>
   /// Get all columns for a certain table
   /// </summary>
   public async Task<List<Tuple<string, string, int?>>> GetAllColumnsFromTableAsync(string tableName)
{
    List<Tuple<string, string, int?>> result;
    string query = "SELECT COLUMN_NAME, DATA_TYPE, CHARACTER_MAXIMUM_LENGTH FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME= @tableName";
    using (IDbConnection db = DbConnection) 
    { 
        IEnumerable<Tuple<string, string, int?>> queryOutput = await db.QueryAsync<string, string, int?, Tuple<string, string, int?>>
            (query, Tuple.Create, new { tableName = tableName }, null, false, splitOn: "*");
        result = queryOutput.ToList(); // System.InvalidOperationException: Invalid attempt to call FieldCount when reader is closed.
    }

    if (result is not null && result.Count > 0)
    {
        return result;
    }
    else
    {
        return default;
    }
}

  • making this a non async method works. so it MUST be "tuple"+"dapper"+"async"

ref: https://github.com/DapperLib/Dapper/issues/745

update:

  1. when replacing the tuple with named tuples the query in itself works.

    public async Task<List<(string COLUMN_NAME, string DATA_TYPE, int? CHARACTER_MAXIMUM_LENGTH)>> GetAllColumnsFromTableAsync2(string tableName)
    {
        const string query = "SELECT COLUMN_NAME, DATA_TYPE, CHARACTER_MAXIMUM_LENGTH FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME= @tableName";
        using var connection = new SqlConnection(_connectionstring);
        var output = await connection.QueryAsync<(string COLUMN_NAME, string DATA_TYPE, int? CHARACTER_MAXIMUM_LENGTH)>
                (query, new { tableName = tableName });
        return output.ToList();
    }
    

I have a further problem but that is another question.


Solution

  • Dapper's supposed to make your life easier, and that code looks complicated.

    If you use a Tuple with named fields, you can just use Dapper's auto mapping to materialize. EG:

    static class DbExtensions
    {
        public static async Task<List<(string COLUMN_NAME, string DATA_TYPE, int? CHARACTER_MAXIMUM_LENGTH)>> GetAllColumnsFromTableAsync(this IDbConnection db, string tableName, string schema = "dbo")
        {
            string query = "SELECT COLUMN_NAME, DATA_TYPE, CHARACTER_MAXIMUM_LENGTH FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME= @tableName and TABLE_SCHEMA=@schema";
            var queryOutput = await db.QueryAsync<(string COLUMN_NAME, string DATA_TYPE, int? CHARACTER_MAXIMUM_LENGTH)>(query, new { tableName = tableName, schema = schema });
            return queryOutput.ToList();
        }
    }
    

    Also note there's a bug in your query if you have multiple tables with the same name, so you should specify the schema.