Report Viewer X Dapper
Asked Answered
B

5

9

I'm feeding a ReportDataSource with a query using Dapper. However, I have an empty report, even with an IEnumerable loaded data. When you spend a Datatable works.

How do I pass data from a query using Dapper for ReportViewer?

this.reportViewer.LocalReport.DataSources.Clear(); 
DataTable dt = new DataTable(); 

dt = CN.Query(Sql, param);

Microsoft.Reporting.WinForms.ReportDataSource rprtDTSource = new Microsoft.Reporting.WinForms.ReportDataSource(dt.TableName, dt); 
this.reportViewer.LocalReport.DataSources.Add(rprtDTSource); 
this.reportViewer.RefreshReport(); –
Busybody answered 19/7, 2013 at 20:58 Comment(0)
C
12

Looks like Dapper now supports the DataTable...

From the test:

public void ExecuteReader()
{
    var dt = new DataTable();
    dt.Load(connection.ExecuteReader("select 3 as [three], 4 as [four]"));
    dt.Columns.Count.IsEqualTo(2);
    dt.Columns[0].ColumnName.IsEqualTo("three");
    dt.Columns[1].ColumnName.IsEqualTo("four");
    dt.Rows.Count.IsEqualTo(1);
    ((int)dt.Rows[0][0]).IsEqualTo(3);
    ((int)dt.Rows[0][1]).IsEqualTo(4);
}

Also now supported is using a DataTable as a TableValueParameter:

public void DataTableParameters()
{
    try { connection.Execute("drop proc #DataTableParameters"); } catch { }
    try { connection.Execute("drop table #DataTableParameters"); } catch { }
    try { connection.Execute("drop type MyTVPType"); } catch { }
    connection.Execute("create type MyTVPType as table (id int)");
    connection.Execute("create proc #DataTableParameters @ids MyTVPType readonly as select count(1) from @ids");

    var table = new DataTable { Columns = { { "id", typeof(int) } }, Rows = { { 1 }, { 2 }, { 3 } } };

    int count = connection.Query<int>("#DataTableParameters", new { ids = table.AsTableValuedParameter() }, commandType: CommandType.StoredProcedure).First();
    count.IsEqualTo(3);

    count = connection.Query<int>("select count(1) from @ids", new { ids = table.AsTableValuedParameter("MyTVPType") }).First();
    count.IsEqualTo(3);

    try
    {
        connection.Query<int>("select count(1) from @ids", new { ids = table.AsTableValuedParameter() }).First();
        throw new InvalidOperationException();
    } catch (Exception ex)
    {
        ex.Message.Equals("The table type parameter 'ids' must have a valid type name.");
    }
}
Characharabanc answered 16/9, 2014 at 14:17 Comment(0)
B
2

As I could not find another way to feed my ReportViewer to a query Dapper.Query then downloaded the source and added the code below.

    #region CODTEC SISTEMAS
    /// <summary>
    /// Return a typed list of objects, reader is closed after the call
    /// </summary>
    public static DataTable Query(this IDbConnection cnn, string sql, object param, IDbTransaction transaction, int? commandTimeout, CommandType? commandType)
    {
        var identity = new Identity(sql, commandType, cnn, typeof(DapperRow), param == null ? null : param.GetType(), null);
        var info = GetCacheInfo(identity);

        IDbCommand cmd = null;
        IDataReader reader = null;

        bool wasClosed = cnn.State == ConnectionState.Closed;
        try
        {
            cmd = SetupCommand(cnn, transaction, sql, info.ParamReader, param, commandTimeout, commandType);

            if (wasClosed) cnn.Open();
            reader = cmd.ExecuteReader(wasClosed ? CommandBehavior.CloseConnection : CommandBehavior.Default);
            wasClosed = false; // *if* the connection was closed and we got this far, then we now have a reader
            // with the CloseConnection flag, so the reader will deal with the connection; we
            // still need something in the "finally" to ensure that broken SQL still results
            // in the connection closing itself

            DataTable dt = new DataTable();
            dt.Load(reader);


            // happy path; close the reader cleanly - no
            // need for "Cancel" etc
            reader.Dispose();
            reader = null;

            return dt;
        }
        finally
        {
            if (reader != null)
            {
                if (!reader.IsClosed) try { cmd.Cancel(); }
                    catch { /* don't spoil the existing exception */ }
                reader.Dispose();
            }
            if (wasClosed) cnn.Close();
            if (cmd != null) cmd.Dispose();
        }
    }
    #endregion
Busybody answered 20/7, 2013 at 15:14 Comment(2)
I know the post is old but you downloaded dapper source and added this method ; Is that correct ?Push
Thats such an overkill if he did. @PushAforetime
S
1

To Get the DataTable from SqliteDB using Dapper.

  1. Pass the query such as "select*from table" inside the method.

  2. list is formed, by executing query.

  3. Then serialize the object into Json and then deserialize the Json into the DataTable.

    public DataTable method(string query)
    {
       string connection = @"Data Source= C:\User\DBFolder\sampleDB.db;Version=3;New=False;Compress=True;";
    
       using (IDbConnection dbConnection = new SQLiteConnection(connection))
        {
            dbConnection.Open();
            var output1 = dbConnection.Query(query).ToList();
            dbConnection.Close();
            var json = JsonConvert.SerializeObject(output1);
            DataTable dt = (DataTable)JsonConvert.DeserializeObject(json, (typeof(DataTable)));
            return dt;
        }
    }
    
Scotney answered 11/1, 2020 at 9:23 Comment(0)
U
0

Not sure how Dapper works, but a datatable is binded like this:

DataTable dt = new DataTable();
DataColumn dc = dt.Columns.Add();
dc.ColumnName = "DataColumn1";
dc = dt.Columns.Add();

dc.ColumnName = "DataColumn2";
dt.Rows.Add(new object[] { "Frank", 32 });
this.reportViewer1.LocalReport.DataSources.Clear();

this.reportViewer1.LocalReport.DataSources.Add(new ReportDataSource("DataSet1_DataTable1", dt));
this.reportViewer1.RefreshReport();

and since I am using C#, I bind data sources like this:

this.bindingSource1.DataSource = getListMethod(); // this method/property returns a list of objects
this.reportViewer1.LocalReport.DataSources.Add(new ReportDataSource("Point", (this.bindingSource1)));
//Point is the datatable name in my Dataset.xsd file
Unbounded answered 19/7, 2013 at 22:11 Comment(1)
With DataTable with no problems. However, the type returned by Dapper does not. I can not give it up once the routine parameters is very complex.Busybody
R
0

Based on this answer, here is my minimal Dapper extension to query SQL Server for a DataTable and optionally pass query parameters:

public static class DapperExtensions
{
    public static async Task<DataTable> QueryDataTable(
        string connectionString, 
        string sql, 
        object? param = null)
    {
        await using var con = new SqlConnection(connectionString);
        await con.OpenAsync();

        var table = new DataTable();
        table.Load(await con.ExecuteReaderAsync(sql, param));

        return table;
    }
}

Example usage:

var table = await QueryDataTable(
    "Server=myServer;Database=myDb;User Id=myUser;Password=myPw", 
    "SELECT * FROM Customers WHERE ID > @id", 
    new { id = 5 });

Another example:

var table = await QueryDataTable(
    "Server=myServer;Database=myDb;User Id=myUser;Password=myPw", 
    "SELECT * FROM Customers WHERE IsLocked = 0");

You might want to change SqlConnection to another database type like e.g. NpgsqlConnection for PostgreSQL.


To query only for a single DataRow, the Dapper extension class could be enhanced like this:

public static async Task<DataRow?> QueryDataRow(
    string connectionString, 
    string sql, 
    object? param = null)
{
    var table = await QueryDataTable(connectionString, sql, param);
    return table.AsEnumerable().FirstOrDefault();
}

And to finish this chain, although it makes no sense at all in the Dapper context, to query for a single value, the Dapper extension class could also contain this method:

public static async Task<object?> QueryValue(
    string connectionString, 
    string sql, 
    object? param = null)
{
    var row = await QueryDataRow(connectionString, sql, param);
    var result = row?[0];
    return result == DBNull.Value ? null : result;
}

Additionally, the QueryValue could also get a generic overload:

public static async Task<T?> QueryValue<T>(
    string connectionString, 
    string sql, 
    object? param = null)
{
    var value = await QueryValue(connectionString, sql, param);
    return value == null ? default : (T)value;
}
Reprieve answered 9/11, 2023 at 21:31 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.