Using ExecuteReader instead of SQLDataAdapter - c#

I've got a C# project where I'm trying to export the results of a datagrid. Sometimes the data gets quite large, so rather than re-executing the code I want to dump the dataset into a session variable.
This works perfectly in most of my projects. One example from a project where I use this is:
protected void Page_Load(object sender, EventArgs e)
{
SqlConnection sqlconnectionStatus = new SqlConnection(str);
string DDL_Value = Convert.ToString(Request.QueryString["DDL_Val"]);
//Use the ClassTesting class to determine if the dates are real, and fill in today's date if they're blank
string StDt_Value = ClassTesting.checkFields(Request.Form["txtStartDate"], "Date");
string EnDt_Value = ClassTesting.checkFields(Request.Form["txtEndDate"], "Date");
//string StDt_Value = Convert.ToString(Request.QueryString["StDt_Val"]);
//string EnDt_Value = Convert.ToString(Request.QueryString["EnDt_Val"]);
string BTN_Value;
// Because the date is stored as an INT, you have to request the string and then
// convert it to an INT
string StDT_Vals = Request.QueryString["StDt_Val"].ToString();
string EnDT_Vals = Request.QueryString["EnDt_Val"].ToString();
//sqlquery = "Select PROC_NM as 'Agent Name', AdminLevel as Role, Count(Claim_ID) as 'Count of Claims Reviewed', Spare as AgentID ";
//sqlquery = sqlquery + "from ClosedClaims_MERGE CCM ";
sqlquery = "Select PROC_NM as 'Agent Name', AdminLevel as Role, Count(DISTINCT Claim_ID) as 'Count of Claims Reviewed', Spare as AgentID ";
sqlquery = sqlquery + "from (SELECT DISTINCT Spare, SpareFinished, CLAIM_ID FROM ClosedClaims_MERGE ";
sqlquery = sqlquery + "UNION SELECT DISTINCT Spare, SpareFinished, CLAIM_ID FROM tblAuditing) CCM ";
sqlquery = sqlquery + "LEFT JOIN PROC_LIST PL ON CCM.Spare = PL.LOGIN ";
sqlquery = sqlquery + "WHERE CCM.SpareFinished >= '" + StDt_Value + "' AND CCM.SpareFinished <= '" + EnDt_Value + "' ";
sqlquery = sqlquery + "GROUP BY Spare, PROC_NM, AdminLevel ";
sqlquery = sqlquery + "ORDER BY Count(Claim_ID) DESC";
SqlConnection con = new SqlConnection(str);
SqlCommand cmd = new SqlCommand(sqlquery, con);
SqlDataAdapter adapter = new SqlDataAdapter(cmd);
// Fill the DataSet.
DataSet ds = new DataSet();
adapter.Fill(ds, "dsEffVol");
// Add this to a session variable so the datagrid won't get NULLed out on repost
Session["SSEffVol"] = ds;
// Perform the binding.
grdEffVol.Attributes.Add("style", "overflow:auto");
//GridView_WODetails.Attributes.Add("style", "table-layout:fixed");
grdEffVol.AutoGenerateColumns = true;
grdEffVol.DataSource = ds;
grdEffVol.DataBind();
}
I've got a new project where I'm not using SQL strings, but instead I'm pulling data based on SQL Server Stored Procedures. The code block there is:
protected void btnSubmit_OnClick(object sender, EventArgs e)
{
List<ReportData> myReportData = new List<ReportData>();
using (SqlConnection connection1 = new SqlConnection(str2))
{
//Query the Reports table to find the record associated with the selected report
using (SqlCommand cmd = new SqlCommand("SELECT * from RM_tblManagerReports WHERE ReportID = " + cboFilterOption.SelectedValue + "", connection1))
{
connection1.Open();
using (SqlDataReader DT1 = cmd.ExecuteReader())
{
while (DT1.Read())
{
//Read the record into an "array", so you can find the SProc and View names
int MyRptID = Convert.ToInt32(DT1[0]);
string MyRptName = DT1[1].ToString();
string MyRptSproc = DT1[2].ToString();
string MySQLView = DT1[3].ToString();
string MyUseDates = DT1[4].ToString();
//Run the Stored Procedure first
SqlConnection connection2 = new SqlConnection(str2);
SqlCommand cmd2 = new SqlCommand();
cmd2.CommandType = CommandType.StoredProcedure;
cmd2.CommandText = "" + MyRptSproc + "";
cmd2.Connection = connection2;
//Set up the parameters, if they exist
if (MyUseDates != "N")
{
cmd2.Parameters.Add("#StDate", SqlDbType.Date).Value = DateTime.Parse(txtStDate.Value);
cmd2.Parameters.Add("#EnDate", SqlDbType.Date).Value = DateTime.Parse(txtEnDate.Value);
}
else
{
}
try
{
connection2.Open();
GridView_Reports.EmptyDataText = "No Records Found";
SqlDataReader dr = cmd2.ExecuteReader(CommandBehavior.CloseConnection);
Session["SSRptMenu"] = dr;
GridView_Reports.DataSource = dr;
GridView_Reports.DataBind();
// Add this to a session variable so the datagrid won't get NULLed out on repost
GridView_Reports.DataBound += GridView_Reports_RowDataBound;
}
catch (Exception ex)
{
ScriptManager.RegisterStartupScript(btnSubmit, typeof(Button), "Report Menu", "alert('There is no View associated with this report.\\nPlease contact the developers and let them know of this issue.')", true);
Console.WriteLine(ex);
return;
}
finally
{
connection2.Close();
connection2.Dispose();
}
}
}
}
}
}
I'm kind of guessing my way through this, and I'm not sure if I'm reading the data into a dataset properly. The page is shutting down, and I'm pretty sure the problem is in the lines:
SqlDataReader dr = cmd2.ExecuteReader(CommandBehavior.CloseConnection);
Session["SSRptMenu"] = dr;
GridView_Reports.DataSource = dr;
Quite honestly, I've googled SqlDataReader vs SqlDataAdapter and can't really find anything, but I need to fill the session variable in the second example and also have the datagrid populate properly. So, in essence, I need to put the results of a Stored Procedure into a dataset. Can anyone offer suggestions on what I'm doing wrong?

I'm pretty sure most controls don't accept readers in their DataSource property. Plus the majority of readers are forward-only, so although you're trying to store the reader as a session variable, chances are you would only be able to read it once.
Why do you want to use a reader for this when your post seems to indicate that you know you need to use a DataSet? Why not just use an adapter the way you show in your first post? Adapters work fine with commands that use sprocs.
Instead of:
SqlDataReader dr = cmd2.ExecuteReader(CommandBehavior.CloseConnection);
Session["SSRptMenu"] = dr;
GridView_Reports.DataSource = dr;
Just use:
var adapter = new SqlDataAdapter(cmd2);
var ds = new DataSet();
adapter.Fill(ds, "MyTableName");
Session["SSRptMenu"] = ds;
GridView_Reports.DataSource = ds;

Related

Error while using NextResult fuction with datareader

Error while using NextResult fuction with datareader
cannot get second table result and error on second NextResult line
"
invalid attempt to call nextresult when reader is closed
"
using (SqlConnection myCon = DBCon)
{
try
{
string Qry = #"SELECT [OPSProcedure],[OPSInsertedOn],[OPSInsertedBy]
FROM [Operation] where OPSID = '" + opId + "';";
Qry += #"SELECT LKCPID FROM dbo.ConcurrentProcedure where CPOperationID = '" + opId + "';";
Qry += #"SELECT IOperaitonID FROM dbo.LkupIntraOperativeAdverseEvents where IOperaitonID = '" + opId + "';";
myCon.Open();
SqlCommand myCommand = new SqlCommand(Qry, myCon);
myCommand.CommandType = CommandType.Text;
SqlDataReader sqlReader = myCommand.ExecuteReader();
DataSet dr = new DataSet();
if (sqlReader.HasRows)
{
dt1.Load(sqlReader);
if(sqlReader.NextResult())
{
dt2.Load(sqlReader);
}
if (sqlReader.NextResult())
{
dt3.Load(sqlReader);
}
}
sqlReader.Close();
}
catch (Exception ex)
{
}
}
What I have tried:
i have tried using below code for multiple result
DataTable.Load closes the sqlReader if sqlReader.IsClosed is false and NextResults returns false as per this forum.
As such, instead of:
if (sqlReader.NextResult())
you need to use:
if (!sqlReader.IsClosed && sqlReader.NextResult() && sqlReader.HasRows)
In this context I would simply use an SqlDataAdapter to make one single call and fill all your tables
using (SqlConnection myCon = DBCon)
{
try
{
string Qry = #"SELECT [OPSProcedure],[OPSInsertedOn],[OPSInsertedBy]
FROM [Operation] where OPSID = #id;
SELECT LKCPID FROM dbo.ConcurrentProcedure
where CPOperationID = #id;
SELECT IOperaitonID FROM dbo.LkupIntraOperativeAdverseEvents
where IOperaitonID = #id";
myCon.Open();
SqlDataAdapter da = new SqlDataAdapter(Qry, myCon);
da.SelectCommand.Parameter.Add("#id", SqlDbType.NVarChar).Value = opID;
DataSet ds = new DataSet();
da.Fill(ds);
// Test...
Console.WriteLine(ds.Tables[0].Rows.Count);
Console.WriteLine(ds.Tables[1].Rows.Count);
Console.WriteLine(ds.Tables[2].Rows.Count);
Notice also that you should never concatenate strings to build sql commands. Always use parameters.

C# & SQL Server : searching all database columns and populating datagrid

I am writing a a C# application, and I am stuck at searching the database and populating a data grid view. However I want to use this with command builder.
The issue is, I need the search to work across all columns in the database. I thought using OR and LIKE statements would do this. But instead I get either invalid syntax or no column name exists in the search.
Does anyone know a solution?
My current .cs:
private void btnSearchJob_Click(object sender, EventArgs e)
{
try
{
SqlConnection con = new SqlConnection();
con.ConnectionString = (#"Data Source=(localdb)\MSSQLLocalDB;Initial Catalog=MTR_Database;Integrated Security=True");
string selectQuery = "SELECT * FROM dbo.[" + cmbJobName.Text + "] WHERE ([Job Name] LIKE " +txtSearchJob.Text+ " OR [Manufacturer] LIKE " +txtSearchJob.Text+ ")";
// DataAdapter
myDA = new SqlDataAdapter(selectQuery, con);
// SqlCommand
SqlCommand myCMD = new SqlCommand(selectQuery, con);
// DataAdapter to Command
myDA.SelectCommand = myCMD;
// Define Datatable
myDT = new DataTable();
// Command Builder (IS GOD!)
SqlCommandBuilder cb = new SqlCommandBuilder(myDA);
// Teach Command builder to be a boss!
myDA.UpdateCommand = cb.GetUpdateCommand();
myDA.InsertCommand = cb.GetInsertCommand();
myDA.DeleteCommand = cb.GetDeleteCommand();
// Fill the DataTable with DataAdapter information
myDA.Fill(myDT);
// Fill DataTable with Database Schema
myDA.FillSchema(myDT, SchemaType.Source);
// Bind The Data Table to the DataGrid
dataGridView1.DataSource = myDT;
// AutoSize Datagrid Rows and Colums to fit the Datagrid
dataGridView1.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.AllCells;
dataGridView1.AutoSizeRowsMode = DataGridViewAutoSizeRowsMode.AllCells;
}
// Catch Exception
catch (Exception ex)
{
MessageBox.Show(this, ex.Message, "SQL ERROR", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
NOTE:
I am aware of using parameters, I am simply using this to see if it will work when I will add parameters later.
this is what I use to get everything back into a DataTable
//'places the call to the system and returns the data as a datatable
public DataTable GetDataAsDatatable(List<SqlParameter> sqlParameters, string connStr, string storedProcName)
{
var dt = new DataTable();
var sqlCmd = new SqlCommand();
using (var sqlconn = new SqlConnection(connStr))
{
sqlCmd.Connection = sqlconn;
sqlCmd.CommandType = CommandType.StoredProcedure;
sqlCmd.CommandText = storedProcName;
sqlCmd.CommandTimeout = 5000;
foreach (var sqlParam in sqlParameters)
{
sqlCmd.Parameters.Add(sqlParam);
}
using (var sqlDa = new SqlDataAdapter(sqlCmd))
{
sqlDa.Fill(dt);
}
}
sqlParameters.Clear();
return dt;
}
//'places the call to the system and returns the data as a datatable
public DataTable GetDataAsDatatable(string connStr, string query)
{
var dt = new DataTable();
var sqlCmd = new SqlCommand();
using (var sqlconn = new SqlConnection(connStr))
{
sqlCmd.Connection = sqlconn;
sqlCmd.CommandType = CommandType.Text;
sqlCmd.CommandText = query;
sqlCmd.CommandTimeout = 5000;
using (var sqlDa = new SqlDataAdapter(sqlCmd))
{
sqlDa.Fill(dt);
}
}
return dt;
}
hopefully this is pretty self explanatory to you, however pass in a list of SQL Parameters, connection string, and the stored procedure name, or use the second one where you pass in the inline SQL and the connection string.
I think you are missing ' in the query. Try this...
string selectQuery = "SELECT * FROM dbo.[" + cmbJobName.Text + "] WHERE ([Job Name] LIKE '" +txtSearchJob.Text+ "' OR [Manufacturer] LIKE '" +txtSearchJob.Text+ "')";

asp.net - searching for a data containing an apostrophe in the database

Good day! I am having a hard time fixing this problem. I've been searching the answer for this but seemed to be very very hard to look for the most fitting answer.
i use this query to search for a tenant's name based on what the user inputs in the txtSearchRP textbox, it works very well to data with no apostrophe in it, however when the user searches for a name containing ' , it does not function well.
example: user inputs MAX'S to search MAX'S RESTAURANT
SELECT * from tenant WHERE (name LIKE '%" + txtSearchRP.Text + "%')
Thanks for your help in advance!
edit for more information:
I am actually passing the query to sqlDataSource to bind the gridview automatically after the user click THE BUTTON.
SqlDataSource3.SelectCommand = SELECT * from tenant WHERE (name LIKE '%" + txtSearchRP.Text + "%')
Try this
conn = new
SqlConnection("ConnectionString");
conn.Open();
SqlCommand cmd = new SqlCommand(
"SELECT * from tenant WHERE (name LIKE #tenant)", conn);
SqlParameter param = new SqlParameter();
param.ParameterName = "#tenant";
param.Value = "%" + txtSearchRP.Text + "%"; // you can use any wildcard operator
cmd.Parameters.Add(param);
SqlDataReader reader = cmd.ExecuteReader();
In addition to the answers already given, in some applications, you might need to consider escaping wildcards such as % in the input string provided by the user.
For example, if the user enters "25%", then matching on "%25%%" will return values that contain "25", rather than restricting to values that contain "25%".
You can escape wildcards as follows (for SQL Server):
string value = ... value entered by user;
value = value.Replace("[", "[[]");
value = value.Replace("_", "[_]");
value = value.Replace("%", "[%]");
Better way create storedprocedure
SP :
Create proc sp_Search( #txtSearch nvarchar(150))
as begin
SELECT * from tenant WHERE name like #txtSearch+'%'
end
Code behind :
string txtSearch = txtSearchRP.Text;
SqlDataReader dr;
using (SqlConnection conn = new SqlConnection(cn.ConnectionString))
{
using (SqlCommand cmdd = new SqlCommand())
{
cmdd.CommandType = CommandType.StoredProcedure;
cmdd.CommandText = "sp_Search";
cmdd.Parameters.AddWithValue("#txtSearch", txtSearch);
cmdd.Connection = conn;
conn.Open();
dr = cmdd.ExecuteReader(CommandBehavior.CloseConnection);
if (dr.HasRows)
{
while (dr.Read())
{
var name = dr["name"].ToString();
var location = dr["location"].ToString();
}
} dr.Close();
conn.Close();
}
}
Updated:
Write a function which returns datatable so that we can bind it to our gridview control as i did in code below
public DataTable bindGridView()
{
string txtSearch = txtSearchRP.Text;
DataTable dt = new DataTable();
using (SqlConnection con = new SqlConnection(cn.ConnectionString))
{
SqlCommand cmdd = new SqlCommand();
cmdd.CommandType = CommandType.StoredProcedure;
cmdd.CommandText = "sp_Search";
cmdd.Parameters.AddWithValue("#txtSearch", txtSearch);
cmdd.Connection = con;
con.Open();
SqlDataAdapter dap = new SqlDataAdapter(cmdd);
DataSet ds = new DataSet();
dap.Fill(ds);
dt = ds.Tables[0];
con.Close();
}
return dt;
}
On Button click : Call bindGridView() function for binding Gridview control
GridView1.DataSource = bindGridView();
GridView1.DataBind();
thanks to all who shared their knowledge and effort, finally got the answer through the String replace method
HERE'S THE 3-LINED CODE
string value = txtSearchRP.Text;
value = value.Replace("'", "['']");
sqlDataSource3.SelectCommand = "SELECT * from tenant WHERE (name LIKE '%" + value.ToString() +"%')";
through the joined effort, answers you posted here, we solve the problem in the simplest form :)

Working on three tier in c# but my insert query is not working

access layer:
public bool AddStudent(string busStudentFullName, string busStudentFatherName)
{
con = new SqlCeConnection();
con.ConnectionString = "data source = C:\\Users\\hasni\\Documents\\Visual Studio 2010\\Projects\\UniversityManagementSystem\\UniversityManagementSystem\\UniversityDB.sdf";
con.Open();
ds1 = new DataSet();
//DataTable t = new DataTable();
// string sql = "SELECT * from AdminPassword where Admin Name ='" + AdminNameLogintextBox.Text + "' and Password='" + PasswordLogintextBox.Text + "'";
//string qry = "SELECT * FROM Students";
// string sql = "SELECT * from AdminPassword where Admin Name ='" + AdminNameLogintextBox.Text + "' and Password='" + PasswordLogintextBox.Text + "'";
string sql = "SELECT * FROM Students";
da = new SqlCeDataAdapter(sql, con);
//da = new SqlCeDataAdapter();
//DataTable t = new DataTable();
//da.Fill(t);
da.Fill(ds1, "Students");
//string userNameDB = Convert.ToString(ds1.Tables[0]);
// return userNameDB;
con.Close();
// string busStudentFullName;
//string busStudentFatherName;
string sql2 = "INSERT INTO Students (Student Full Name,Student Father Name) Values('"+ busStudentFullName + "','" + busStudentFatherName + "')";
da = new SqlCeDataAdapter(sql2, con);
da.Fill(ds1, "Students");
con.Close();
return true;
}
Business layer:
public bool getResponseForAddStudent(string studentName, string studentfathername)
{
bool var = access.AddStudent(studentName, studentfathername);
return var;
}
Presentation layer:
private void AddStudentButton_Click(object sender, EventArgs e)
{
string studentName = StudentNameBox.Text;
string studentfathername = StdFatherNameBox.Text;
bool var = _busGeneral.getResponseForLogin(studentName, studentfathername);
if (var)
{
MessageBox.Show("Student Added");
}
else
{
MessageBox.Show("Sorry");
}
}
Your Sql isn't valid, when columns have spaces in their name need to surround with square brackets: INSERT INTO Students (Student Full Name,Student Father Name)
instead needs to be
INSERT INTO Students ([Student Full Name],[Student Father Name])
If I understand what you are attempting to do, there are a number of issues with your code in addition to the brackets problem.
Firstly, you are closing the connection before performing the last DataAdapter.Fill operation.
And since you want to insert a record before (re)filling the DataAdapter with student data, you must first issue a ExecuteNonQuery statement using a SqlCeCommand object. Also, to avoid injection attacks and other problems you should ALWAYS use parameterized queries. I would also advise wrapping the code with try...catch to handle errors.
Here is what I think you are trying to achieve with the insert operation (I have only desk-checked the syntax):
// con.Close();
// string busStudentFullName;
SqlCeCommand cmd = db.CreateCommand();
cmd.CommandText = "INSERT INTO Students ([Student Full Name],[Student Father Name]) Values(#FullName, #DadsName)";
cmd.AddParameter("#FullName", busStudentFullName);
cmd.AddParameter("#DadsName", busStudentFatherName);
cmd.ExecuteNonQuery();
At this point you can fill the DataAdapter with student rows including the newly inserted one.

How duplicate entries be prevented while using SqlBulkCopy?

I am importing excel data into sql server using SqlbulkCopy, but the problem is i want to prevent any duplicate records being entered. Is there a way by which duplicate can be ignored or deleted automatically?
protected void Button1_Click(object sender, EventArgs e)
{
string strFileType = System.IO.Path.GetExtension(FileUpload1.FileName).ToString().ToLower();
string strFileName = FileUpload1.PostedFile.FileName.ToString();
FileUpload1.SaveAs(Server.MapPath("~/Import/" + strFileName + strFileType));
string strNewPath = Server.MapPath("~/Import/" + strFileName + strFileType);
string excelConnectionString = String.Format(#"Provider=Microsoft.Jet.OLEDB.4.0;" + "Data Source=" + strNewPath + "; Extended Properties=Excel 8.0;");
// Create Connection to Excel Workbook
using (OleDbConnection connection = new OleDbConnection(excelConnectionString))
{
var command = new OleDbCommand("Select ID,Data FROM [Sheet1$]", connection);
connection.Open();
// Create DbDataReader to Data Worksheet
using (DbDataReader dr = command.ExecuteReader())
{
// SQL Server Connection String
string sqlConnectionString = "Data Source=ARBAAZ-1B14C081;Initial Catalog=abc;Integrated Security=True";
con.Open();
DataTable dt1 = new DataTable();
string s = "select count(*) from ExcelTable";
string r = "";
SqlCommand cmd1 = new SqlCommand(s, con);
try
{
SqlDataAdapter da1 = new SqlDataAdapter(cmd1);
da1.Fill(dt1);
}
catch
{
}
int RecordCount;
RecordCount = Convert.ToInt32(cmd1.ExecuteScalar());
r = RecordCount.ToString();
Label1.Text = r;
con.Close();
int prv = Convert.ToInt32(r);
// Bulk Copy to SQL Server
using (SqlBulkCopy bulkCopy = new SqlBulkCopy(sqlConnectionString))
{
bulkCopy.DestinationTableName = "ExcelTable";
SqlBulkCopyColumnMapping mapping1 = new SqlBulkCopyColumnMapping("excelid", "id");
SqlBulkCopyColumnMapping mapping2 = new SqlBulkCopyColumnMapping("exceldata", "data");
bulkCopy.ColumnMappings.Add(mapping1);
bulkCopy.ColumnMappings.Add(mapping2);
bulkCopy.WriteToServer(dr);
}
con.Open();
DataTable dt = new DataTable();
s = "select count(*) from ExcelTable";
r = "";
SqlCommand cmd = new SqlCommand(s, con);
try
{
SqlDataAdapter da = new SqlDataAdapter(cmd);
da.Fill(dt);
}
catch
{
}
RecordCount = Convert.ToInt32(cmd.ExecuteScalar());
r = RecordCount.ToString();
Label1.Text = r;
con.Close();
int ltr = Convert.ToInt32(r);
if (prv == ltr)
{
Label1.Text = "No records Added";
}
else
{
Label1.Text = "Records Added Successfully !";
}
}
}
}
Can this problem be fixed by creating a trigger to delete duplicates? if yes how? i am a newbie i have never created a trigger.
Another problem is no matter what i try i am unable to get column mapping to work. I am unable to upload data when column names in excel sheet and database are different.
You can create INDEX
CREATE UNIQUE INDEX MyIndex ON ExcelTable(id, data) WITH IGNORE_DUP_KEY
And when you will insert data with bulk to db, all duplicate values will not inserted.
no, either you manage it on your dr object on you code before you load it into the db (like running a DISTINCT operation) or you create a trigger on the DB to check. The trigger will reduce the bulk insert's performace though.
Another option would be to bulk insert into a temp table and then insert into your destination table FROM your temp table using a select DISTINCT
as far as I remember,
you could filter out the redundant rows when importing from the excel file itself.
the following SQL query should be used in the OleDbCommand constructor.
var command = new OleDbCommand("Select DISTINCT ID,Data FROM [Sheet1$]", connection);

Categories

Resources