Report Viewer X Dapper - c#

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(); –

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.");
}
}

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

To Get the DataTable from SqliteDB using Dapper.
Pass the query such as "select*from table" inside the method.
list is formed, by executing query.
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;
}
}

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

Related

DataTable Rows Count Null Exception

Im trying to fill a listview with data from my database but get a null reference exception when i load the listview. Im using MVC pattern.
Code in my data acces layer:
public List<Menu_Item> DB_Get_All_MenuItems()
{
string query = "SELECT * FROM [Menu_Items]";
SqlParameter[] sqlParameters = new SqlParameter[0];
return ReadTables(ExecuteSelectQuery(query, sqlParameters));
}
private List<Menu_Item> ReadTables(DataTable dataTable)
{
List<Menu_Item> menu_items = new List<Menu_Item>();
foreach (DataRow dr in dataTable.Rows)
{
Menu_Item menu_Item = new Menu_Item()
{
Menu_ID = (int)dr["Menu_ID"],
Naam = (string)dr["Naam"],
Prijs = (float)dr["Prijs"],
Voorraad = (int)dr["Voorraad"],
Categorie_ID = (int)dr["Catogorie_ID"]
};
menu_items.Add(menu_Item);
}
return menu_items;
}
I have checked and my SQL parameters are all spelled correctly
The exact error is: datatable.rows = 'datatable.rows' threw an exception of type 'system.nullreferenceexception'
My DateTable method looks like this
protected DataTable ExecuteSelectQuery(string query, params SqlParameter[] sqlParameters)
{
SqlCommand command = new SqlCommand();
DataTable dataTable;
DataSet dataSet = new DataSet();
try
{
command.Connection = OpenConnection();
command.CommandText = query;
command.Parameters.AddRange(sqlParameters);
command.ExecuteNonQuery();
adapter.SelectCommand = command;
adapter.Fill(dataSet);
dataTable = dataSet.Tables[0];
}
catch (SqlException e)
{
return null;
throw new Exception(e.ToString());
}
finally
{
CloseConnection();
}
return dataTable;
}
Check whether dataTable null or not
if(dataTable!=null && dataTable.Rows.Count > 0)
{
foreach (DataRow dr in dataTable.Rows)
{
Menu_Item menu_Item = new Menu_Item()
{
Menu_ID = (int)dr["Menu_ID"],
Naam = (string)dr["Naam"],
Prijs = (float)dr["Prijs"],
Voorraad = (int)dr["Voorraad"],
Categorie_ID = (int)dr["Catogorie_ID"]
};
menu_items.Add(menu_Item);
}
}
check if the datarows.count is null or not ...
Also in DB_Get_All_MenuItems() mention dbconnction string, sql command type, and datareader and all...first make sure the datas is returning in this function o/p....
You are trying to use something that is null. This means you either set it to null, or you never set it to anything at all.
Now, You can then place a breakpoint at every found location and run your program with the debugger attached. Every time the debugger breaks on such a breakpoint, you need to determine whether you expect the reference to be non-null.

how to fill DataTable from stored procedure

I am trying to execute a stored procedure with parameters, and get the results into a DataTable; but after execution, I get this:
this is the screen
I can get IDataRecord but I can't IDataReader - why? What do I miss? Thanks
public DataTable ExecuteProcedureTest(string storedProcedure, Dictionary<Filter, object> filterValue)
{
var parameters = GetParams(filterValue);
var sqlConnection = new SqlConnection(ConfigurationManager.ConnectionStrings["testconn"].ConnectionString);
var cmd = sqlConnection.CreateCommand();
SqlDataReader reader;
IDataReader dataReader;
IDataRecord[] dataRecords;
var table = new DataTable();
cmd.CommandType = CommandType.StoredProcedure;
cmd.CommandText = storedProcedure;
foreach (var item in parameters)
{
cmd.Parameters.Add(item);
}
try
{
sqlConnection.Open();
reader = cmd.ExecuteReader();
dataRecords = reader.OfType<IDataRecord>().ToArray();
table.Load(reader);
}
catch (Exception ex)
{
throw ex;
}
finally
{
sqlConnection.Dispose();
}
//var count = dataRecords.Count();
return table;
}
If your goal is to fill a DataTable from a SqlCommand, use a SqlDataAdapter instead of a SqlDataReader. Here is a handy extension method:
public static class SqlExtensions
{
public static DataTable ExecuteDataTable(this SqlCommand command)
{
DataTable table = new DataTable();
using (SqlDataAdapter dataAdapter = new SqlDataAdapter(command))
{
dataAdapter.Fill(table);
}
return table;
}
}
Then from your code you can use it like this:
try
{
sqlConnection.Open();
table = cmd.ExecuteDataTable();
}
finally
{
sqlConnection.Dispose();
}

SqlHelper DataAdapter

I am using a SqlHelper class which has common methods for CRUD operations.
public static void Fill(DataSet dataSet, String procedureName)
{
SqlConnection oConnection = new SqlConnection(DBInterface.ConnectionString);
SqlCommand oCommand = new SqlCommand(procedureName, oConnection);
oCommand.CommandType = CommandType.StoredProcedure;
SqlDataAdapter oAdapter = new SqlDataAdapter();
oAdapter.SelectCommand = oCommand;
oConnection.Open();
using (SqlTransaction oTransaction = oConnection.BeginTransaction())
{
try
{
oAdapter.SelectCommand.Transaction = oTransaction;
oAdapter.Fill(dataSet);
oTransaction.Commit();
}
catch
{
oTransaction.Rollback();
throw;
}
finally
{
if (oConnection.State == ConnectionState.Open)
oConnection.Close();
oConnection.Dispose();
oAdapter.Dispose();
}
}
}
Now in my code, I am calling this method as,
private void BindCustomers()
{
DataSet dsCust = new DataSet();
SqlHelper.Fill(dsCust, "getCustomers");
--then I bind this dataset to datagridview
}
This all works fine. Now I want to update the data in the database. But I am confused how do I call DataAdatpaer.Update(dataset) here to update the changes made in datagridview into database. Is this possible here? Or I need to do it conventionally to find the updated row and call the ExecuteNonQuery function in the SqlHelper? Is there anything which can be done to use dataadapter.update(ds)
Thanks
You don't need to hide data adapter, or if for any reason you did so, you need to expose a method in your class to push updates to server.
Example
Public class SqlHelper
{
string commandText;
string connectionString;
public SqlHelper(string command, string connection)
{
commandText = command;
connectionString = connection;
}
public DataTable Select()
{
var table = new DataTable();
using (var adapter = new SqlDataAdapter(this.commandText, this.connectionString))
adapter.Fill(table)
return table;
}
public void Update(DataTable table)
{
using (var adapter = new SqlDataAdapter(this.commandText, this.connectionString))
{
var builder = new SqlCommandBuilder(adapter);
adapter.Update(table);
}
}
}
By calling this method in your code you can perform all crud operation select, update, delete and insert. you just need to pass connection string, procedure name and parameters list. Using this method you will retrieve data in the DataTable.if anyone want Dataset(Multiple Result) then just need to replace DataSet on the place of DataTable
SqlConnection conn = new SqlConnection("Your ConnectionString");
public DataTable ExecuteDataTable(string ProcedureName, SqlParameter[] _Param)
{
try
{
DataTable dataTable = new DataTable();
SqlCommand cmd = new SqlCommand(ProcedureName, conn);
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Clear();
if (_Param is not null)
{
for (int i = 0; i < _Param.Length; i++)
{
if (_Param[i].ParameterName is not null)
{
if (_Param[i].Value is not null)
{
cmd.Parameters.AddWithValue(_Param[i].ParameterName, _Param[i].Value);
}
else
{
cmd.Parameters.AddWithValue(_Param[i].ParameterName, DBNull.Value);
}
}
}
}
conn.Open();
SqlDataAdapter DA = new SqlDataAdapter(cmd);
DA.Fill(dataTable);
conn.Close();
return dataTable;
}
catch (Exception ex)
{
conn.Close();
throw;
}
}

Bulk Update in C#

For inserting a huge amount of data in a database, I used to collect all the inserting information into a list and convert this list into a DataTable. I then insert that list to a database via SqlBulkCopy.
Where I send my generated list LiMyList which contain information of all bulk data which I want to insert to database and pass it to my bulk insertion operation
InsertData(LiMyList, "MyTable");
Where InsertData is
public static void InsertData<T>(List<T> list,string TableName)
{
DataTable dt = new DataTable("MyTable");
clsBulkOperation blk = new clsBulkOperation();
dt = ConvertToDataTable(list);
ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.PerUserRoamingAndLocal);
using (SqlBulkCopy bulkcopy = new SqlBulkCopy(ConfigurationManager.ConnectionStrings["SchoolSoulDataEntitiesForReport"].ConnectionString))
{
bulkcopy.BulkCopyTimeout = 660;
bulkcopy.DestinationTableName = TableName;
bulkcopy.WriteToServer(dt);
}
}
public static DataTable ConvertToDataTable<T>(IList<T> data)
{
PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(typeof(T));
DataTable table = new DataTable();
foreach (PropertyDescriptor prop in properties)
table.Columns.Add(prop.Name, Nullable.GetUnderlyingType(prop.PropertyType) ?? prop.PropertyType);
foreach (T item in data)
{
DataRow row = table.NewRow();
foreach (PropertyDescriptor prop in properties)
row[prop.Name] = prop.GetValue(item) ?? DBNull.Value;
table.Rows.Add(row);
}
return table;
}
Now I want to do an update operation, is there any way as for inserting data is done by SqlBulkCopy for Updating data to DataBase From C#.Net
What I've done before is perform a bulk insert from the data into a temp table, and then use a command or stored procedure to update the data relating the temp table with the destination table. The temp table is an extra step, but you can have a performance gain with the bulk insert and massive update if the amount of rows is big, compared to updating the data row by row.
Example:
public static void UpdateData<T>(List<T> list,string TableName)
{
DataTable dt = new DataTable("MyTable");
dt = ConvertToDataTable(list);
using (SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["SchoolSoulDataEntitiesForReport"].ConnectionString))
{
using (SqlCommand command = new SqlCommand("", conn))
{
try
{
conn.Open();
//Creating temp table on database
command.CommandText = "CREATE TABLE #TmpTable(...)";
command.ExecuteNonQuery();
//Bulk insert into temp table
using (SqlBulkCopy bulkcopy = new SqlBulkCopy(conn))
{
bulkcopy.BulkCopyTimeout = 660;
bulkcopy.DestinationTableName = "#TmpTable";
bulkcopy.WriteToServer(dt);
bulkcopy.Close();
}
// Updating destination table, and dropping temp table
command.CommandTimeout = 300;
command.CommandText = "UPDATE T SET ... FROM " + TableName + " T INNER JOIN #TmpTable Temp ON ...; DROP TABLE #TmpTable;";
command.ExecuteNonQuery();
}
catch (Exception ex)
{
// Handle exception properly
}
finally
{
conn.Close();
}
}
}
}
Notice that a single connection is used to perform the whole operation, in order to be able to use the temp table in each step, because the scope of the temp table is per connection.
In my personal experience, the best way to handled this situation is utilizing a Stored Procedure with a Table-Valued Parameter and a User-Defined Table Type. Just set up the type with the columns of the data table, and pass in said-data table as a parameter in the SQL command.
Within the Stored Procedure, you can either join directly on some unique key (if all rows you are updating exist), or - if you might run into a situation where you are having to do both updates and inserts - use the SQL Merge command within the stored procedure to handle both the updates and inserts as applicable.
Microsoft has both syntax reference and an article with examples for the Merge.
For the .NET piece, it's a simple matter of setting the parameter type as SqlDbType.Structured and setting the value of said-parameter to the Data Table that contains the records you want to update.
This method provides the benefit of both clarity and ease of maintenance. While there may be ways that offer performance improvements (such as dropping it into a temporary table then iterating over that table), I think they're outweighed by the simplicity of letting .NET and SQL handle transferring the table and updating the records itself. K.I.S.S.
Bulk Update:
Step 1: put the data which you want to update and primary key in a list.
Step 2: pass this list and ConnectionString to BulkUpdate Method As shown below
Example:
//Method for Bulk Update the Data
public static void BulkUpdateData<T>(List<T> list, string connetionString)
{
DataTable dt = new DataTable("MyTable");
dt = ConvertToDataTable(list);
using (SqlConnection conn = new SqlConnection(connetionString))
{
using (SqlCommand command = new SqlCommand("CREATE TABLE
#TmpTable([PrimaryKey],[ColumnToUpdate])", conn))
{
try
{
conn.Open();
command.ExecuteNonQuery();
using (SqlBulkCopy bulkcopy = new SqlBulkCopy(conn))
{
bulkcopy.BulkCopyTimeout = 6600;
bulkcopy.DestinationTableName = "#TmpTable";
bulkcopy.WriteToServer(dt);
bulkcopy.Close();
}
command.CommandTimeout = 3000;
command.CommandText = "UPDATE P SET P.[ColumnToUpdate]= T.[ColumnToUpdate] FROM [TableName Where you want to update ] AS P INNER JOIN #TmpTable AS T ON P.[PrimaryKey] = T.[PrimaryKey] ;DROP TABLE #TmpTable;";
command.ExecuteNonQuery();
}
catch (Exception ex)
{
// Handle exception properly
}
finally
{
conn.Close();
}
}
}
}
Step 3: put The ConvertToDataTable Method as shown Below.
Example:
public static DataTable ConvertToDataTable<T>(IList<T> data)
{
PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(typeof(T));
DataTable table = new DataTable();
foreach (PropertyDescriptor prop in properties)
table.Columns.Add(prop.Name, Nullable.GetUnderlyingType(prop.PropertyType) ?? prop.PropertyType);
foreach (T item in data)
{
DataRow row = table.NewRow();
foreach (PropertyDescriptor prop in properties)
row[prop.Name] = prop.GetValue(item) ?? DBNull.Value;
table.Rows.Add(row);
}
return table;
}
Notes: WhereEver SquareBracket[] is there, put your own value.
Try out SqlBulkTools available on Nuget.
Disclaimer: I'm the author of this library.
var bulk = new BulkOperations();
var records = GetRecordsToUpdate();
using (TransactionScope trans = new TransactionScope())
{
using (SqlConnection conn = new SqlConnection(ConfigurationManager
.ConnectionStrings["SqlBulkToolsTest"].ConnectionString))
{
bulk.Setup<MyTable>()
.ForCollection(records)
.WithTable("MyTable")
.AddColumn(x => x.SomeColumn1)
.AddColumn(x => x.SomeColumn2)
.BulkUpdate()
.MatchTargetOn(x => x.Identifier)
.Commit(conn);
}
trans.Complete();
}
Only 'SomeColumn1' and 'SomeColumn2' will be updated. More examples can be found here
I would insert new values in a temporary table and then do a merge against the destination table, something like this:
MERGE [DestTable] AS D
USING #SourceTable S
ON D.ID = S.ID
WHEN MATCHED THEN
UPDATE SET ...
WHEN NOT MATCHED
THEN INSERT (...)
VALUES (...);
You could try to build a query that contains all data. Use a case. It could look like this
update your_table
set some_column = case when id = 1 then 'value of 1'
when id = 5 then 'value of 5'
when id = 7 then 'value of 7'
when id = 9 then 'value of 9'
end
where id in (1,5,7,9)
I'd go for a TempTable approach because that way you aren't locking anything. But if your logic needs to be only in the front end and you need to use bulk copy, I'd try a Delete/Insert approach but in the same SqlTransaction to ensure integrity which would be something like this:
// ...
dt = ConvertToDataTable(list);
using (SqlConnection cnx = new SqlConnection(myConnectionString))
{
using (SqlTranscation tran = cnx.BeginTransaction())
{
DeleteData(cnx, tran, list);
using (SqlBulkCopy bulkcopy = new SqlBulkCopy(cnx, SqlBulkCopyOptions.Default, tran))
{
bulkcopy.BulkCopyTimeout = 660;
bulkcopy.DestinationTableName = TabelName;
bulkcopy.WriteToServer(dt);
}
tran.Commit();
}
}
Complete answer, disclaimer: arrow code; this is mine built from research; Published in SqlRapper. It uses custom attributes over properties to determine whether a key is primary. Yes, super complicated. Yes super reusable. Yes, needs to be refactored. Yes, it is a nuget package. No, the documentation isn't great on github, but it exists. Will it work for everything? Probably not. Will it work for simple stuff? Oh yeah.
How easy is it to use after setup?
public class Log
{
[PrimaryKey]
public int? LogId { get; set; }
public int ApplicationId { get; set; }
[DefaultKey]
public DateTime? Date { get; set; }
public string Message { get; set; }
}
var logs = new List<Log>() { log1, log2 };
success = db.BulkUpdateData(logs);
Here's how it works:
public class PrimaryKeyAttribute : Attribute
{
}
private static bool IsPrimaryKey(object[] attributes)
{
bool skip = false;
foreach (var attr in attributes)
{
if (attr.GetType() == typeof(PrimaryKeyAttribute))
{
skip = true;
}
}
return skip;
}
private string GetSqlDataType(Type type, bool isPrimary = false)
{
var sqlType = new StringBuilder();
var isNullable = false;
if (Nullable.GetUnderlyingType(type) != null)
{
isNullable = true;
type = Nullable.GetUnderlyingType(type);
}
switch (Type.GetTypeCode(type))
{
case TypeCode.String:
isNullable = true;
sqlType.Append("nvarchar(MAX)");
break;
case TypeCode.Int32:
case TypeCode.Int64:
case TypeCode.Int16:
sqlType.Append("int");
break;
case TypeCode.Boolean:
sqlType.Append("bit");
break;
case TypeCode.DateTime:
sqlType.Append("datetime");
break;
case TypeCode.Decimal:
case TypeCode.Double:
sqlType.Append("decimal");
break;
}
if (!isNullable || isPrimary)
{
sqlType.Append(" NOT NULL");
}
return sqlType.ToString();
}
/// <summary>
/// SqlBulkCopy is allegedly protected from Sql Injection.
/// Updates a list of simple sql objects that mock tables.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="rows">A list of rows to insert</param>
/// <param name="tableName">a Table name if your class isn't your table name minus s.</param>
/// <returns>bool success</returns>
public bool BulkUpdateData<T>(List<T> rows, string tableName = null)
{
var template = rows.FirstOrDefault();
string tn = tableName ?? template.GetType().Name + "s";
int updated = 0;
using (SqlConnection con = new SqlConnection(ConnectionString))
{
using (SqlCommand command = new SqlCommand("", con))
{
using (SqlBulkCopy sbc = new SqlBulkCopy(con))
{
var dt = new DataTable();
var columns = template.GetType().GetProperties();;
var colNames = new List<string>();
string keyName = "";
var setStatement = new StringBuilder();
int rowNum = 0;
foreach (var row in rows)
{
dt.Rows.Add();
int colNum = 0;
foreach (var col in columns)
{
var attributes = row.GetType().GetProperty(col.Name).GetCustomAttributes(false);
bool isPrimary = IsPrimaryKey(attributes);
var value = row.GetType().GetProperty(col.Name).GetValue(row);
if (rowNum == 0)
{
colNames.Add($"{col.Name} {GetSqlDataType(col.PropertyType, isPrimary)}");
dt.Columns.Add(new DataColumn(col.Name, Nullable.GetUnderlyingType(col.PropertyType) ?? col.PropertyType));
if (!isPrimary)
{
setStatement.Append($" ME.{col.Name} = T.{col.Name},");
}
}
if (isPrimary)
{
keyName = col.Name;
if (value == null)
{
throw new Exception("Trying to update a row whose primary key is null; use insert instead.");
}
}
dt.Rows[rowNum][colNum] = value ?? DBNull.Value;
colNum++;
}
rowNum++;
}
setStatement.Length--;
try
{
con.Open();
command.CommandText = $"CREATE TABLE [dbo].[#TmpTable]({String.Join(",", colNames)})";
//command.CommandTimeout = CmdTimeOut;
command.ExecuteNonQuery();
sbc.DestinationTableName = "[dbo].[#TmpTable]";
sbc.BulkCopyTimeout = CmdTimeOut * 3;
sbc.WriteToServer(dt);
sbc.Close();
command.CommandTimeout = CmdTimeOut * 3;
command.CommandText = $"UPDATE ME SET {setStatement} FROM {tn} as ME INNER JOIN #TmpTable AS T on ME.{keyName} = T.{keyName}; DROP TABLE #TmpTable;";
updated = command.ExecuteNonQuery();
}
catch (Exception ex)
{
if (con.State != ConnectionState.Closed)
{
sbc.Close();
con.Close();
}
//well logging to sql might not work... we could try... but no.
//So Lets write to a local file.
_logger.Log($"Failed to Bulk Update to Sql: {rows.ToCSV()}", ex);
throw ex;
}
}
}
}
return (updated > 0) ? true : false;
}

How to return sql query result messages with c# ASP.net

I tried following code but I couldn't find success messages from it.Here commandText can be insert,update,delete or select query and I need dataset if it is a select query else success messages like in sql result output("12 row(s) inserted successfully").I can't use ExecuteScalar or ExecuteNonQuery methods since I need dataset output when select query executed.
public static DataSet ExecuteDataset(string connectionString, CommandType commandType, string commandText, params SqlParameter[] commandParameters)
{
using (SqlConnection cn = new SqlConnection(connectionString))
{
cn.InfoMessage += delegate(object sender, SqlInfoMessageEventArgs e)
{
MessageInfo += "\n" + e.Message;
};
cn.FireInfoMessageEventOnUserErrors = true;
cn.Open();
using (SqlCommand command = new SqlCommand(commandText, cn))
{
using (SqlDataAdapter adapter = new SqlDataAdapter(command))
{
DataSet dt = new DataSet();
adapter.Fill(dt); // Do something with DataTable
return dt;
}
}
}
}
I am using System.Data version 4.0.0.0 and the following works for me with UPDATE, INSERT, DELETE and SELECT statements.
using (SqlDataAdapter adapter = new SqlDataAdapter(command))
{
var rowsAffected = -1;
adapter.SelectCommand.StatementCompleted += delegate (object sender, StatementCompletedEventArgs e)
{
rowsAffected = e.RecordCount;
};
DataSet dt = new DataSet();
adapter.Fill(dt); // Do something with DataTable
return dt;
}
I managed to solve it by following way but hope there should be a better way.
string type = Query.Trim().Split(' ')[0].ToLower();
if ( type == "select")
{
DataSet result = SqlHelper.ExecuteDataset(CommandType.Text, Query);
}
else
{
if(type == "insert") type="inserted."; else type=type+"d.";
int a=(int)SqlHelper.ExecuteNonQuery(CommandType.Text, Query);
if (a > 0)
{
lblInfo.Text = "Query executed successfully. " + a.ToString() + " row(s) " + type;
}
}
I found another one.That's better than above one.
query = resultQuery + " SELECT '('+CONVERT(VARCHAR(10),##ROWCOUNT)+' row(s) affected)' AS RESULT";
DataSet result = DataSet result = SqlHelper.ExecuteDataset(CommandType.Text, query);
if (result.DefaultViewManager.DataViewSettingCollectionString.Contains("Table") == true)
{
sqlGrid.Visible = true;
sqlGrid.DataSource = result;
sqlGrid.DataBind();
}
if (sqlGrid.Rows.Count==1 && sqlGrid.Rows[0].Cells.Count==1)
{
string text = (sqlGrid.Rows[0].Cells[0]).Text;
if (text.Contains("row(s) affected"))
{
lblInfo.Text=text;
lblInfo.Visible=true;
sqlGrid.DataSource = null;
sqlGrid.DataBind();
}
}

Categories