Adapter.Fill takes long - c#

I've created a RadGrid Programmatically and binding it using NeedDataSource -> GetDataTable.
Within the GetDataTable, I'm calling my connstring and fill the grid with an adapter (see code below). Problem is that, in my SQL Server, the query takes 0 sec to run, but in the ASP.NET debug mode, it's taking about 3~5s, in my case of having a lot of RadGrids on the page, this is causing my page to load slowly.
Is this processing speed of adapter.Fill a general issue or have I done something wrong with the setting? (ie, orders of conn.open/close or any others)?
public DataTable GetDataTable(int Year, int month, string datatype)
{
String ConnString = ConfigurationManager.ConnectionStrings["IHG_MSTConnectionString"].ConnectionString;
SqlConnection conn = new SqlConnection(ConnString);
SqlDataAdapter adapter = new SqlDataAdapter();
adapter.SelectCommand = new SqlCommand("[Yield_Planner_With_Strategy]", conn);
adapter.SelectCommand.CommandType = System.Data.CommandType.StoredProcedure;
adapter.SelectCommand.Parameters.AddWithValue("#Holidex_Code", RadComboBox_Hotels.SelectedValue);
adapter.SelectCommand.Parameters.AddWithValue("#Event_Year", Year);
adapter.SelectCommand.Parameters.AddWithValue("#Event_Month", month);
adapter.SelectCommand.Parameters.AddWithValue("#DataType", datatype);
adapter.SelectCommand.Parameters.AddWithValue("#MktSeg", Fruitful.Get_Checked_Values_As_CSV(RadComboBox_MktSeg));
string exportdate = DateTime.Now.ToString("yyyy/MM/dd");
if (RadComboBox_ExportTimeStamp.Text != "" && RadComboBox_ExportTimeStamp.Text != "Create New Strategy")
{ exportdate = Convert.ToDateTime(RadComboBox_ExportTimeStamp.Text).ToString("yyyy/MM/dd"); }
adapter.SelectCommand.Parameters.AddWithValue("#ExportTimeStamp", exportdate);
DataTable myDataTable = new DataTable();
conn.Open();
try
{
adapter.Fill(myDataTable);
}
finally
{
conn.Close();
}
return myDataTable;
}

Why do you use a string for the ExportTimeStamp parameter? Use
DateTime if it's a date or datetime column.
I'd also replace all of your calls to AddWithValue with Add. When you call AddWithValue it has to guess what the type of your parameter is. If it guesses wrong the optimizer cannot select the correct index and falls back to a table scan, and that speaks to the core of database performance.
AddWithVaue may result in multiple query plans. Since .NET doesn't know what the size of the database column is, it will use the size of the variable. so if you have a parameterized query and pass two strings in, one of length 10, the other of length 20, you will get two plans: #text nvarchar(10) and #text nvarchar(20). It will also assume that your field is nvarchar when it may be varchar and you will get an implicit conversion.
So always either pass the correct type to AddWithValue or (better) use SqlParameterCollection.Add with the correct type and size. It'll also validate the parameter before it gets sent to the database.
Related:
SqlCommand Parameters Add vs. AddWithValue
Bad habits to kick : mis-handling date
The Seven Sins against TSQL Performance
Also, use the using-statement to ensure that the connection gets closed as soon as you're finished with it - even in case of an error.
Here is an example:
public DataTable GetDataTable(int Year, int month, string datatype)
{
DataTable myDataTable = new DataTable();
String ConnString = ConfigurationManager.ConnectionStrings["IHG_MSTConnectionString"].ConnectionString;
using(SqlConnection conn = new SqlConnection(ConnString))
using (SqlDataAdapter adapter = new SqlDataAdapter())
{
var cmd = new SqlCommand("[Yield_Planner_With_Strategy]", conn);
cmd.CommandType = System.Data.CommandType.StoredProcedure;
cmd.Parameters.Add("#Holidex_Code", SqlDbType.Int).Value = int.Parse(RadComboBox_Hotels.SelectedValue);
cmd.Parameters.Add("#Event_Year", SqlDbType.Int).Value = Year;
cmd.Parameters.Add("#Event_Month", SqlDbType.Int).Value = month;
cmd.Parameters.Add("#DataType", SqlDbType.VarChar).Value = datatype;
cmd.Parameters.Add("#MktSeg", SqlDbType.NVarChar).Value = Fruitful.Get_Checked_Values_As_CSV(RadComboBox_MktSeg);
DateTime exportdate = DateTime.Now;
if (RadComboBox_ExportTimeStamp.Text != "" && RadComboBox_ExportTimeStamp.Text != "Create New Strategy")
{
exportdate = DateTime.Parse(RadComboBox_ExportTimeStamp.Text);
}
cmd.Parameters.Add("#ExportTimeStamp", SqlDbType.DateTime).Value = exportdate;
adapter.SelectCommand = cmd;
// you don't need to open it with Fill
adapter.Fill(myDataTable);
}
return myDataTable;
}

Related

How to resolve operand data type nvarchar is invalid for minus operator

I've a SQL query which tries to fetch all records that were created within the last one hour
string query = "select * from Monarchchangelog mcl WHERE LOWER(mcl.mcl_usercomment) LIKE 'bolt%' AND mcl.mcl_createtime > DATEADD(MINUTE, -#minutesBack, GETDATE()) ORDER BY mcl.mcl_createtime DESC";
string tablename = "NEW_UPDATES";
string minutesBack = "60"; //set by another function but for eg sake I've hardcoded the value
SqlCommand command = new SqlCommand(query);
command.Parameters.AddWithValue("minutesBack", minutesBack);
DataSet ds = RunQuery(command, tablename);
which I'm executing with the below code
private DataSet RunQuery(SqlCommand command, String tablename)
{
DataSet ds = null;
SqlDataAdapter adapter = null;
using (SqlConnection oc = new SqlConnection(CONNECTIONSTRING))
{
try
{
oc.Open();
command.CommandType = CommandType.Text;
command.Connection = oc;
adapter = new SqlDataAdapter(command);
ds = new DataSet();
adapter.Fill(ds, tablename);
}
catch
{
if (ds != null)
{
ds.Dispose();
}
}
finally
{
if (oc != null)
{
oc.Close();
oc.Dispose();
}
}
}
return ds;
}
when I try to execute this code I'm getting the below error but mcl_createtime is of datetime datatype
operand data type nvarchar is invalid for minus operator
Can someone let me know where am I going wrong
Thank you
With the help of #DaleK's comment I resolved the issue, replaced the code
command.Parameters.AddWithValue("minutesBack", minutesBack);
with this piece
command.Parameters.Add("minutesBack", SqlDbType.Int).Value = minutesBack;
A good way of passing your parameters is by explicitly mentioning the sql datatype to avoid datatype issues like this. For more details please follow the link AddWithValue vs Add.
So I passed my parameter as a number and not a string, also got a good understanding of try-catch, using statements.
Good learning, thank you everyone for your contribution.

sql query to show data from price between two columns C#

EDIT: I am not able to format my code below, if any one can fix it.
I am new to sql queries and still learning.
Table Name: CommissionSetupTable.
I want to display #Paisa if gross_amount is between the range of #FromRate and #ToRate
Below is my code:
string paisa;
private void load_commission_setup()
{
SqlCeConnection conn = null;
SqlCeCommand cmd = null;
SqlCeDataReader rdr = null;
try
{
conn =
new SqlCeConnection(
#"Data Source=|DataDirectory|\Database.sdf;Persist Security Info=False");
conn.Open();
int rowindex = purchaseBillTableDataGridView.Rows.Count - 1;
gross_amount = double.Parse(purchaseBillTableDataGridView[10, rowindex].Value.ToString());
// Gross Amount is between the ranges of FromRate and ToRate.
cmd = new SqlCeCommand("SELECT Paisa FROM CommissionSetupTable WHERE='" + gross_amount.ToString() + "' BETWEEN #FromRate AND #ToRate;", conn);
rdr = cmd.ExecuteReader();
if (rdr == null)
{
}
else
{
while (rdr.Read())
{
paisa = rdr["Paisa"].ToString();
}
rdr.Close();
cmd.Dispose();
}
}
finally
{
conn.Close();
int rowindex = purchaseBillTableDataGridView.Rows.Count - 1;
purchaseBillTableDataGridView[11, rowindex].Value = paisa;
}
}
The correct syntax to use here is the following
cmd = new SqlCeCommand(#"SELECT Paisa FROM CommissionSetupTable
WHERE #gross BETWEEN FromRate AND ToRate;", conn);
Notice that the two field names should not be prefixed with #, otherwise they will be considered parameters placeholders.
And now, before executing the command, add the parameter for the #gross placeholder
cmd.Parameters.Add("#gross", SqlDbType.Decimal).Value = gross_amount;
I don't know what is the exact datatype of the columns FromRate and EndRate, but
note that you should use the correct datatype for your parameter. Do not pass a string and expect the database engine do the conversion for you. (or worse concatenate your value to the rest of the sql using ToString()). This is always wrong also if sometime the database engine could understand your values.
EDIT
Also, following your comments below, it appears that this line is wrong
int rowindex = purchaseBillTableDataGridView.Rows.Count - 1;
If your DataGridView has the property AllowUserToAddRow set to True then you want to use
int rowindex = purchaseBillTableDataGridView.Rows.Count - 2;
because the first line points to the empty row added to the DataGridView for inserting a new record.

DataTable Always Returns 0

My query returns results, but for some reason my DataTable always shows 0. The only thing I altered was the fact that I added parameters to the C# syntax (altho if I manually run the stored procedure it returns results). This is my syntax, does anyone see something that is incorrect syntactically in it?
protected void btnPress1464()
{
RunSQLStoredProc();
DataTable tableA = ebdb.Tables[0];
if (this.dtgAttendanceTracker.Items.Count == 0)
{
this.gvwTest.DataSource = tableA
this.gvwTest.DataBind();
}
}
public DataSet RunSQLStoredProc()
{
ebdb = new DataSet();
SqlQueryBuilder = new StringBuilder();
SqlQueryBuilder.Append("exec alphadawg ");
ebdb = DoThis(SqlQueryBuilder.ToString());
return ebdb;
}
public DataSet DoThis(string sqlQuery, int employeeid, DateTime hiredate, DateTime terminationdate)
{
try
{
System.Configuration.ConnectionStringSettings connstring = System.Configuration.ConfigurationManager.ConnectionStrings["SQLServer1"];
using (SqlConnection conn = new SqlConnection(connstring.ConnectionString))
{
using (SqlCommand cmd = new SqlCommand())
{
cmd.CommandText = sqlQuery;
cmd.Connection = conn;
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.AddWithValue("#employeeid", employeeid.ToString());
cmd.Parameters.AddWithValue("#hiredate", hiredate.ToShortDateString());
cmd.Parameters.AddWithValue("#terminationdate", terminationdate.ToShortDateString());
conn.Open();
SqlDataAdapter adapter = new SqlDataAdapter(cmd);
adapter.Fill(ebdb);
conn.Close();
}
}
return ebdb;
}
catch (Exception exception) { throw exception; }
}
The CommandText should only contain the stored-procedure name and not also exec if the command's CommandType is StoredProcedure. The StringBuilder is also redundant.
I also think that the way how you use AddWithValue with the wrong types could cause this issue(look at the last paragraph of my answer):
So not
SqlQueryBuilder = new StringBuilder();
SqlQueryBuilder.Append("exec alphadawg ");
ebdb = DoThis(SqlQueryBuilder.ToString());
but
ebdb = DoThis("alphadawg", otherParamaters...);
It's also bad practice to pass a sql-string to a method that executes it, that often introduces sql injection issues. You should not have a method DoThis but GetAlphaDawg which encapsulates the sql-query and only pass the parameter-values.
Apart from that, why do you return the DataSet from a method if it's actually a field in your class that you return? Instead initialize and fill it in the method, that's much clearer and also prevents issues when you load an already filled dataset(data will be appended by default).
This would be a possible implementation. Note that you shouldn't use AddWithValue and don't use String for DateTime but always use the correct type, all the more if you use AddWithValue which needs to infer the type from the value:
public DataSet GetAlphaDawg(int employeeid, DateTime hiredate, DateTime terminationdate)
{
DataSet dsAlpha = new DataSet();
try
{
System.Configuration.ConnectionStringSettings connstring = System.Configuration.ConfigurationManager.ConnectionStrings["SQLServer1"];
using (var conn = new SqlConnection(connstring.ConnectionString))
{
using (var da = new SqlDataAdapter("alphadawg", conn))
{
da.SelectCommand.CommandType = CommandType.StoredProcedure;
var parameter = da.SelectCommand.Parameters;
parameter.Add("#employeeid", SqlDbType.Int).Value = employeeid;
parameter.Add("#hiredate", SqlDbType.Date).Value = hiredate;
parameter.Add("#terminationdate", SqlDbType.Date).Value = terminationdate;
da.Fill(dsAlpha); // Open/Close not needed with Fill
return dsAlpha;
}
}
} catch (Exception) { throw; }
}
Since you use ToShortDateString, if you actually want to remove the time portion of your DateTime use DateTime.Date, for example:
parameter.Add("#hiredate", SqlDbType.Date).Value = hiredate.Date;

Login page in asp.net. Issue with query?

I would like to create a simple login page in asp.net. here is my code:
protected void Button1_Click(object sender, EventArgs e)
{
SqlConnection conn = new SqlConnection();
conn.ConnectionString = "Data Source=TEST-PC\\SQLSERVER2012;Initial Catalog=oncf;Integrated Security=True";
conn.Open();
string query = "SELECT COUNT(*) FROM Account WHERE acc_username= '" + TextBox1.Text + "' AND acc_password= '" + TextBox2.Text + "'";
SqlCommand cmd = new SqlCommand(query, conn);
SqlDataReader myreader = cmd.ExecuteReader();
int count = 0;
while(myreader.Read())
{
count = count + 1;
}
if(count==1)
{
Response.Redirect("page2.aspx");
}
else
{
Label1.Visible = true;
}
conn.Close();
}
I set a counter in order to know if the credentials entered are present in the DB. If the value of the counter goes to 1, the login is successful. Otherwise, the label with a message error is displayed!
However, whatever I enter as input in the username and login textboxes, it always redirect me to the other page !
For now, my concern is not the security aspects, I just want to test this simple code, I don't see any problem with the code, but still it doesnt work, it is driving me crazy...
The reason that you are always redirecting is that your reader always returns 1 row, whether there is a match or not. If there is a match in your database, then the query will return
(no column name)
---------------
1
If there is not a match then it will return:
(no column name)
---------------
0
Either way, myreader.Read() will return true, and you will increment count in this part:
while(myreader.Read())
{
count = count + 1;
}
if(count==1)
{
Response.Redirect("page2.aspx");
}
Rather than checking the if the query returns rows you can retrieve the value of the count using SqlCommand.ExecuteScalar(). In addition to this I would make three more changes:
1. Use parameterised queries
This is not just a security concern, parameterised queries are able to use cached plans, whereas if you concatenate the parameters into the query then a new plan is compliled for each new variable value. In addition, parameterised queries are more strongly typed, and you don't need to escape things like O'shea to ensure that your extra quote doesn't mess up the query.
2. Encrypt the passwords
This is directly to do with security so should really be overlooked as per your request to not comment on security, HOWEVER, this answer is not just for your benefit, and a half answer is likely to be read by someone in the future who may or may not be aware of the risks of storing plain text passwords. There is a simple encryption method in this answer.
3. Add using blocks to your code
A minor change, but when you have objects that implement IDisposable it is a good idea to use a using block to esnure they are disposed of properly.
So you might end up with:
string password = SomeStaticClass.Encrypt(TextBox2.Text);
string connectionString = "Data Source=TEST-PC\\SQLSERVER2012;Initial Catalog=oncf;Integrated Security=True";
string query = "SELECT UserCount = COUNT(*) FROM Account WHERE acc_username= #UserName AND acc_password= #Password";
using (var connection = new SqlConnection(connectionString))
using (var command = new SqlCommand(query, connection))
{
connection.Open();
command.Parameters.Add("#UserName", SqlDbType.VarChar, 50).Value = TextBox1.Text;
command.Parameters.Add("#Password", SqlDbType.VarChar, 50).Value = password;
int count = Convert.ToInt32(command.ExecuteScalar());
if(count==1)
{
Response.Redirect("page2.aspx");
}
else
{
Label1.Visible = true;
}
}
The problem you are experiencing is because the followinq query Always returns one row even if there isn't a match in the database:
SELECT COUNT(*) FROM Account WHERE acc_username=....
If there is no match, you get a row with one column, value 0.
You are checking the number of rows returned when you should just be checking the return value.
Use this instead
int count = Convert.ToInt32(cmd.ExecuteScalar());
if(count==1)
{
Response.Redirect("page2.aspx");
}
else
{
Label1.Visible = true;
}
I know you said you don't want advice on security but just to be sure:
Don't store passwords plain text in a database. Always hash them using a salt.
Don't use string concatenation when building sql. Use parameters.
don't use ExecuteReader when you want to return a single value, use ExecuteScalar:
int count = int.Pares(cmd.ExecuteScalar().toString());
if(count >= 1)
{
Response.Redirect("page2.aspx");
}
else
{
Label1.Visible = true;
}
You should always use Paremeterized queries Using parameters in SQL statements
string username=TextBox1.Text;
string password=TextBox2.Text;
SqlConnection conn = new SqlConnection();
conn.ConnectionString = "Data Source=TEST-PC\\SQLSERVER2012;Initial Catalog=oncf;Integrated Security=True";
conn.Open();
SqlCommand cmd = new SqlCommand("SELECT * FROM Account WHERE acc_username=#username and
AND acc_password=#password", conn);
cmd.Parameters.AddWithValue("#username",username);
cmd.Parameters.AddWithValue("#password",password);
SqlDataAdapter da = new SqlDataAdapter(cmd);
DataTable dt = new DataTable();
da.Fill(dt);
if (dt.Rows.Count > 0)
{
Response.Redirect("page2.aspx");
}
else
{
Label1.Visible = true;
}
Try adding if (myreader.HasRows) before while(myreader.Read())

Updating through SqlDataAdapter and DataSet in C#

I have the fol code
string user = "new user";
DataSet myDS = new DataSet();
string sql = string.Format("Select Counter,Occupants From Rooms where Room = '{0}'",room);
SqlDataAdapter dAdapt = new SqlDataAdapter(sql, cnn);
dAdapt.Fill(myDS, "Rooms");
foreach (DataTable dt in myDS.Tables)
{
int var =(int) dt.Rows[0].ItemArray[0];
var--;
dt.Rows[0].ItemArray[0] = var;
String occups = dt.Rows[0].ItemArray[1].ToString();
occups += user;
dt.Rows[0].ItemArray[1] = occups;
}
dAdapt.Update(myDS,"Rooms");
I'm retrieving a single row with two columns-- Counter(small int type) and Occupants(text type). I get an error saying that the data types text and var char are incompatible in the equal to operator But the error is pointed to the line dAdapt.Fill(myDS, "Rooms"); which is weird. What's wrong here? And I'm pretty sure that the db connection is open as I've checked it by printing the connection status.
This won't work anyway unless you have specified an Update-Command for the DataAdaper.
I would not load the record into memory to update it. Meanwhile it could have been changed from another transaction. It's inefficient anyway. Instead i would use a single update-command:
string updateSql = #"
UPDATE ROOMS SET
Counter = Counter + 1,
Occupants = Occupants + ',' + #newUser
WHERE
Room = #Room";
using(var con = new SqlConnection(connectionString))
using (var updateCommand = new SqlCommand(updateSql, con))
{
updateCommand.Parameters.AddWithValue("#newUser", user);
updateCommand.Parameters.AddWithValue("#Room", room);
con.Open();
updateCommand.ExecuteNonQuery();
}
The problem is in your select, because you can use the syntax, that Room = 'something', because text is not compatible with =.
Use LIKE instead of equal sign (=).
Fixed query should look like:
SELECT Counter,Occupants FROM Rooms WHERE Room LIKE '{0}'
But I recommand to use SqlParameters instead of string.Format, because it is not secure.

Categories

Resources