Reusable code to retrieve data - asp.net

i have found an very good method for retrieving any result set from the database just by specifying the stored procedure name.i think the code is very much reusable.code is as follows
using System.Data;
using System.Data.SqlClient;
private DataSet GetFreshData(string sprocName)
{
using ( SqlConnection conn = new SqlConnection() )
{
using ( SqlDataAdapter da = new SqlDataAdapter() )
{
da.SelectCommand = new SqlCommand();
da.SelectCommand.CommandText = sprocName;
da.SelectCommand.CommandType = CommandType.StoredProcedure;
da.SelectCommand.Connection = conn;
DataSet ds = new DataSet();
try
{
da.SelectCommand.Connection.Open();
da.Fill(ds);
da.SelectCommand.Connection.Close();
}
catch
{
return null;
}
finally
{
// do other things...calling Close() or Dispose()
// for SqlConnection or SqlDataAdapter objects not necessary
// as its taken care of in the nested "using" statements
}
return ds;
}
}
}
my question is can someone suggest a modification to this method when the stored procedure need to specify several parameters

Easy! :) take a SqlParameter[] as the second argument to the function.
Then make sure da.SelectCommand.Parameters is filled with the list of SqlParameter objects in the SqlParameter[]

Related

SQL Server connection in .net

Here is my code.
public Dataset ReturnDataset()
{
SqlConnection con = new SqlConnection(Proper connectionstring);
SqlCommand cmd = new SqlCommand(spname,con);
Dataset ds = new Dataset();
try
{
con.Open();
cmd.CommandType = CommandType.StoreProcedure;
SqlDataAdapter da = new SqlDataAdapter(cmd);
da.Fill(ds,table);
return ds;
}
catch (TimeoutException Ex)
{
con.Close();
if (Ex.Message.Contains("Timeout expired"))
{
ds = null;
return ds;
}
else
{
throw;
}
}
catch (Exception)
{
throw;
}
Does I need to write finally clause to close connection if error occur or not?In first try block I closed connection and then throw exception.Does I need to do same in second block?What will happen If already closed connection and trying to close once again?
It is always good idea to close your connection on finally part of try catch as below, or use using statment and let .net to take care of it :
try{
}
catch{
}
finally{
conn.close();
}
or use using :
using (SqlConnection connection = new SqlConnection(connectionString))
{
}
You can close on error too. If you are worried about the state of connection before closing you can check if it is open and close it in finally:
if (conn != null && conn.State == ConnectionState.Open)
{
conn.close();
}
If not strictly necessary I avoid to create a structured catch cascade.
If necessary, I always try to define a catch cascade from the more specific exception to the more generic exception.
I always put any cleanup logic in the finally block:
SqlConnection con = new SqlConnection(Proper connectionstring);
SqlCommand cmd = new SqlCommand(spname,con);
Dataset ds = new Dataset();
try
{
con.Open();
cmd.CommandType = CommandType.StoreProcedure;
SqlDataAdapter da = new SqlDataAdapter(cmd);
da.Fill(ds,table);
return ds;
}
catch(TimeoutException toEx)
{
//manage or log specific exception
}
catch(Exception ex)
{
//manage or log generic exception
}
finally
{
//cleanup
con.Close();
ds = null;
}
I would rewrite your code to be something like this:
public DataSet ReturnDataset()
{
// initialize return value
DataSet result = null;
// put SqlConnection and SqlCommand into using () { ....} blocks to ensure proper disposal
using (SqlConnection con = new SqlConnection(Proper connectionstring))
using (SqlCommand cmd = new SqlCommand(spname, con))
{
result = new DataSet();
try
{
con.Open();
// you had a typo: it's a StoredProcedure - not a StoreProcedure
cmd.CommandType = CommandType.StoredProcedure;
SqlDataAdapter da = new SqlDataAdapter(cmd);
da.Fill(result, table);
con.Close();
}
catch (TimeoutException Ex)
{
if (Ex.Message.Contains("Timeout expired"))
{
result = null;
}
else
{
throw;
}
}
}
// return the result - null if an error happened, a valid DataSet otherwise
return result;
}
Points I improved:
declare the possible return value at the beginning, return only once at the very end
removed unnecessary "catch" on an exception you don't do anything with -> just let it happen
put SqlConnection and SqlCommand into using(...) { ... } blocks to ensure proper and speedy disposal
Why don't you use the Using Statement that ensures a proper closing and disposing of the disposable objects? I think you don't need to catch the exceptions at this level, just let them bubble up and, if needed take care of them at the upper level
Here you can simply
public Dataset ReturnDataset()
{
Dataset ds = new Dataset();
using(SqlConnection con = new SqlConnection(Proper connectionstring))
using(SqlCommand cmd = new SqlCommand(spname,con))
{
con.Open();
cmd.CommandType = CommandType.StoreProcedure;
using(SqlDataAdapter da = new SqlDataAdapter(cmd))
{
da.Fill(ds,table);
}
}
return ds;
}
If this code raises an exception the dataset will be never returned and being a local variable will be quickly become eligible for garbage collection
A better option is to use the using keyword, that does the closing / disposing for you
public Dataset ReturnDataset()
{
using (SqlConnection con = new SqlConnection(connectionstring))
using (SqlCommand cmd = new SqlCommand(spname,con))
{
Dataset ds = new Dataset();
try
{
con.Open();
cmd.CommandType = CommandType.StoreProcedure;
SqlDataAdapter da = new SqlDataAdapter(cmd);
da.Fill(ds,table);
}
catch (TimeoutException Ex)
{
ds = null;
}
return ds;
}
}

return type of SqlDataReader method

I have a method inside a class, that contains SqlDataReader(it connects to a database table and retrieves a number of rows).
later i want to assign each record that SqlDataReader has, to asp labels . but i don't know what should be the return type of a method , so i can extract the values from it, and how to do that in code behind file.
here is the code;
public (???) displayCustoemrhShipOrder()
{
string htmlStr = string.Empty;
string sConnectionString = ConfigurationManager.ConnectionStrings["LGDB"].ToString();
SqlConnection SqlCOn = new SqlConnection(sConnectionString);
SqlCommand SqlCmd = new SqlCommand();
SqlCmd.Connection = SqlCOn;
SqlCmd.CommandText = "displayCustomerShipOrder";
SqlCmd.CommandType = CommandType.StoredProcedure;
SqlCOn.Open();
SqlCmd.Parameters.AddWithValue("ShipOrderID",shipOrderID);
SqlDataReader reader = SqlCmd.ExecuteReader();
while (reader.Read())
{
htmlStr = reader.GetInt32(0).ToString();
}
reader.Close();
SqlCOn.Close();
return(???)
}
In general, you should never return SqlDataReader to a layer outside of your database-access code. The reason behind this general principle is that SqlDataReader is a very "expensive" object that keeps an open connection to the database! So if you keep a SqlDataReader around, it's not so bad, but if you keep 10 lying around at any given time? what about 100 of them? 1000? It's a recipe for disaster.
So how do you close the connection to the database from a SqlDataReader? .NET has a very handy methods in the IDisposable interface called Dispose() that will take care of the cleanup for you. So your code might looks like
function getData()
{
// instantiate SqlDataReader from SqlCommand, call it "rdr"
rdr.Dispose();
}
But actually there's an issue with that code. What if your code throws an exception before it gets to Dispose()? It should really look like
function getData()
{
try{
// instantiate SqlDataReader from SqlCommand, call it "rdr"
}
catch(Exception){}
Finally{
rdr.Dispose();
}
}
That's very verbose! What if you don't want to type all that? Don't worry, .NET handled this for you as well. Do this:
public (???) displayCustoemrhShipOrder()
{
string htmlStr = string.Empty;
string sConnectionString = ConfigurationManager.ConnectionStrings["LGDB"].ToString();
using(SqlConnection SqlCOn = new SqlConnection(sConnectionString))
{
using(SqlCommand SqlCmd = new SqlCommand())
{
SqlCmd.Connection = SqlCOn;
SqlCmd.CommandText = "displayCustomerShipOrder";
SqlCmd.CommandType = CommandType.StoredProcedure;
SqlCOn.Open();
SqlCmd.Parameters.AddWithValue("ShipOrderID",shipOrderID);
using(SqlDataReader reader = SqlCmd.ExecuteReader())
while (reader.Read())
{
htmlStr = reader.GetInt32(0).ToString();
}
// reader.Close(); // this line becomes optional, Dispose() will call Close()
}
// SqlCOn.Close(); // this line becomes optional, Dispose() will call Close()
}
}
return(???)
}
This is all a roundabout way of saying that if you know you need to Dispose() and Close() the SqlDataReader, and that you cannot get data out of it once you do that, then you obviously should not return it to code what needs to work with the data (but not necessarily the database connection that it was taking up). I suggest using a DataTable class, you can read about it here:
.NET DataTable class
Also the method to convert to a DataTable is called DataTable.Load(SqlDatareader...
You can add each individual value to List<string> return it and use outside of the method.

Passing SqlParameter in webmethod asp.net

I am trying to create a web service which will help to execute stored procedure. And that web method I am calling in my code to execute a stored procedure. This is my web method -
[WebMethod(Description = des_ExecuteParamerizedSelectCommand)]
public DataTable ExecuteParamerizedSelectCommand(string CommandName, CommandType cmdType, SqlParameter[] param)
{
DataTable table = new DataTable();
using (SqlConnection con = new SqlConnection(ConnectionString()))
{
using (SqlCommand cmd = con.CreateCommand())
{
cmd.CommandType = cmdType;
cmd.CommandText = CommandName;
cmd.Parameters.AddRange(param);
try
{
if (con.State != ConnectionState.Open)
{
con.Open();
}
using (SqlDataAdapter da = new SqlDataAdapter(cmd))
{
da.Fill(table);
}
}
catch
{
throw;
}
}
}
return table;
}
Now this is my code in my data access layer - when I am trying to call this web method, its throwing compile time error.
Error 2 Argument 2: cannot convert from 'System.Data.CommandType' to 'DAL.sqlDBHelper.CommandType'
Error 3 Argument 3: cannot convert from 'System.Data.SqlClient.SqlParameter[]' to 'DAL.sqlDBHelper.SqlParameter[]'
My code to call the webmethod -
sqlDBHelper.ODCdbHelper mysqlDBHelper = new sqlDBHelper.ODCdbHelper();
public Login GetUserRoles(string _Idsid)
{
Login login = null;
SqlParameter[] parameters = new SqlParameter[]
{
new SqlParameter("#UserName", _Idsid)
};
//Lets get the list of all employees in a datataable
using (DataTable table = mysqlDBHelper.ExecuteParamerizedSelectCommand("GetUserRole", CommandType.StoredProcedure, parameters))
Can you please tell me someone, where I am wrong??
Thanks in advance
Gulrej
Try like this
DAL.sqlDBHelper.SqlParameter[] parameters = new DAL.sqlDBHelper.SqlParameter[]//Change Here {
SqlParameter("#UserName", _Idsid)
};
//Lets get the list of all employees in a datataable
using (DataTable table = mysqlDBHelper.ExecuteParamerizedSelectCommand("GetUserRole", DAL.sqlDBHelper.CommandType.StoredProcedure, parameters))
I presume DAL.sqlDBHelper.CommandType will be an enumerator in your data access layer.
And the expected parameter is DAL.sqlDBHelper.SqlParameter[] instead of System.Data.SqlClient.SqlParameter[]
So you might call the select function as
DAL.sqlDBHelper.SqlParameter[] parameters = new DAL.sqlDBHelper.SqlParameter[]
{
new SqlParameter("#UserName", _Idsid)
};
using (DataTable table = mysqlDBHelper.ExecuteParamerizedSelectCommand("GetUserRole", DAL.sqlDBHelper.CommandType.StoredProcedure, parameters))
Please check what is the command type defined for stored procedures in your DAL.

Dataaccess help in three tier asp.net architecture

I have DAL function as
public DataTable executeSelectQuery(String _query, SqlParameter[] sqlParameter)
{
SqlCommand myCommand = new SqlCommand();
DataTable dataTable = new DataTable();
dataTable = null;
DataSet ds = new DataSet();
try
{
myCommand.Connection = openConnection();
myCommand.CommandText = _query;
myCommand.Parameters.AddRange(sqlParameter);
myCommand.ExecuteNonQuery();
myAdapter.SelectCommand = myCommand;
myAdapter.Fill(ds);
dataTable = ds.Tables[0];
}
catch (SqlException e)
{
Console.Write("Error - Connection.executeSelectQuery - Query:
" + _query + " \nException: " + e.StackTrace.ToString());
return null;
}
finally
{
}
return dataTable;
}
My button click
public void save_click(object sender,EventArgs e)
{
try
{
string query = "Insert into customer_master(customer_title,customer_name)values(#parameter1,#parameter2)";
SqlParameter[] sqlparam = new SqlParameter[2];
sqlparam[0] = new SqlParameter("#parameter1", SqlDbType.VarChar, 50);
sqlparam[0].Value = ddl_title.SelectedValue;
sqlparam[1] = new SqlParameter("#parameter2", SqlDbType.VarChar, 50);
sqlparam[1].Value = txt_group_name.Text;
string id = ms.insert(query, sqlparam);
catch(Exception ex)
{
throw ex;
}
}
I want the button click function values to passed not as sqlparameter but as sql command object.How to do this with sqlcommand object instead of sqlparameter.
Why do you want to pass around SqlCommand objects?? I wouldn't consider that an improvement of your current code - in the contrary!
Your UI code should really only pass values to the DAL function - just a List<int> or two strings or something. The DAL should do all the database-related stuff like creating SqlCommand and SqlParameters
So in your case here, I would have a method InsertCustomer on your DAL something like this:
public void InsertCustomer(string customerName, string customerTitle)
{
.... // do all the DB stuff here - create SqlCommand, fill in SqlParameter,
// execute the query
}
and your UI code should call this like this:
MyDAL dal = new MyDAL();
dal.InsertCustomer(txt_group_name.Text, ddl_title.SelectedValue);
There should be no trace whatsoever of ADO.NET classes or function in your UI code layer! So don't create SqlCommand or even SqlParameter in your UI layer - encapsulate this in the DAL layer! That what it's for!

Invalid attempt to call Read when reader is closed

I'm kind of struggling trying to get this datagrid to bind. Everytime I run my code, I get an error message stating, "Invalid attempt to call Read when reader is closed". I don't see where I am closing my reader. Can you please help me? My code for loading the datagrid is below:
protected void LoadGrid()
{
using (SqlConnection conn = new SqlConnection())
{
conn.ConnectionString = ConfigurationManager.ConnectionStrings["VTC"].ConnectionString;
conn.Open();
string sql = "select * from roi_tracking";
using (SqlCommand cmd = new SqlCommand(sql, conn))
{
using (SqlDataReader sqlReader = cmd.ExecuteReader())
{
gridROI.DataSource = sqlReader;
gridROI.DataBind();
sqlReader.Dispose();
cmd.Dispose();
}
}
}
}
You can't use a SqlDataReader as a DataSource for a DataGrid.
From MSDN:
A data source must be a collection that implements either the
System.Collections.IEnumerable interface (such as
System.Data.DataView, System.Collections.ArrayList, or
System.Collections.Generic.List(Of T)) or the IListSource interface to
bind to a control derived from the BaseDataList class.
Datasource property:
http://msdn.microsoft.com/en-us/library/system.web.ui.webcontrols.basedatalist.datasource.aspx
SqlDataReader:
http://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqldatareader.aspx
A common methodology to bind the query results to your datagrid would be to use a SqlDataAdapter to fill a DataTable or DataSet and then bind that to your DataGrid.DataSource:
protected void LoadGrid()
{
using (SqlConnection conn = new SqlConnection())
{
conn.ConnectionString = ConfigurationManager.ConnectionStrings["VTC"].ConnectionString;
conn.Open();
string sql = "select * from roi_tracking";
using (SqlCommand cmd = new SqlCommand(sql, conn))
{
SqlDataAdapter adapter = new SqlDataAdapter();
adapter.SelectCommand = cmd;
adapter.Fill((DataTable)results);
gridROI.DataSource = results;
}
}
}
In addition to what pseudocoder said, you wouldn't want to bind to a SqlDataReader anyway: the connection to the database will remain open so long as the reader instance exists.
You definitely want to deserialize the data into some other disconnected data structure so that you release the connection back into the pool as quickly as possible.

Resources