public class SqlHelper
{
public SqlHelper()
{
}
public static SqlConnection GetConnection()
{
SqlConnection conn = new SqlConnection();
conn.ConnectionString = #"Data Source=.\SQLEXPRESS;AttachDbFilename=" + System.Web.HttpContext.Current.Server.MapPath(#"~\App_Data\learn.mdf") + ";Integrated Security=True;User Instance=True";
return conn;
}
public static SqlDataReader ExecuteReader(string sql)
{
SqlConnection con = GetConnection();
con.Open();
SqlCommand cmd = new SqlCommand(sql, con);
SqlDataReader dr = null;
try
{
dr = cmd.ExecuteReader(CommandBehavior.CloseConnection);
}
catch
{
con.Close();
return null;
}
return dr;
}
public static Object ExecuteScalar(string sql)
{
SqlConnection con = GetConnection();
con.Open();
SqlCommand cmd = new SqlCommand(sql, con);
Object val = null;
try
{
val = cmd.ExecuteScalar();
}
catch
{
con.Close();
return null;
}
finally
{
con.Close();
}
return val;
}
public static DataSet ExecuteDataSet(string sql)
{
SqlConnection con = GetConnection();
SqlCommand cmd = new SqlCommand(sql, con);
DataSet ds = new DataSet();
SqlDataAdapter adapt = new SqlDataAdapter(cmd);
try
{
adapt.Fill(ds);
}
catch
{
con.Close();
}
return ds;
}
public static void ExecuteNonQuery(string sql)
{
SqlConnection con = GetConnection();
con.Open();
SqlCommand cmd = new SqlCommand(sql, con);
try
{
cmd.ExecuteNonQuery();
}
finally
{
con.Close();
}
}
}
This is the Class which I use to implement every access to my database . But I think that the way I do connection with the database is a little bit overblown cause I have to hit the Connect function every time I need something . As well as other users going to do the same which kills the performance.
So what is the perfect way to connect with the database - and to stay connected if that better . Note that I use the database in many pages!
Thanks
First, you should be using "using" statements to ensure that all your ADO.NET objects are properly disposed of in the event of a failure:
public static void ExecuteNonQuery(string sql)
{
using(var con = GetConnection())
{
con.Open();
using(var cmd = new SqlCommand(sql, con))
{
cmd.ExecuteNonQuery();
}
}
}
However, having said that, I don't really see a problem with this approach. The advantage is that the connections, commands, adapters and whatnot are properly disposed of every time you execute a bit of SQL. If you were to make a single static SqlConnection instance, you'd escalate the chances that the connection is already in use (when, for example, iterating over the contents of a SqlDataReader).
If you are really concerned about it, provide overloads that take a connection as an extra parameter:
public static void ExecuteNonQuery(string sql, SqlConnection connection)
{
using(var cmd = new SqlCommand(sql, con))
{
cmd.ExecuteNonQuery();
}
}
This way, callers can either execute a bit of SQL that doesn't require multiple calls, or they can call your GetConnectionMethod to obtain a connection, and pass it to multiple calls.
If this is used for a web site then you have to consider that between requests for pages, even from the same browser, your server state will be torn down (in general terms) so there's nothing really to be gained from trying to maintain your SQL connection between pages. That's the first thing.
If each page is the result of a single database connection then you are probably as optimised as you really need to be, if you are making several connections over the generation of a page then you may want to look at keeping a connection alive until you have finished retrieving data; either by maintaining the connection or optimising your data retrieval to limit the back and forth between your app and the db.
Maintaining a database connection is the job of the connection pool, and not the connection consumer. The best practice is to aquire a connection as late as possible and release it as soon as possible.
using(var connection = new SqlConnection(YourConnectionStringHelperFunction())
{
}
One thing that YOu might take into consideration is the Dependency Injection PAttern and some IoC controller. If every page needs to have this connection make this an injectable property (constructor probably wont work unless You implement some kind of infrastructure classes like Request) use some container (Unity, Castle, StructureMap) pack the needed things up (maybe cache, maybe some other things) and let the container do the magic (by magic I mean tons of boilerplate code) for You.
luke
First you can write a seperate class like this :
Get method for getting data (with a Select query) and Set method for manipulating data (Insert, Update, Delete)
using System.Data;
using System.Data.Odbc;
using System.Data.SqlClient; //using this you can replace instead odbc to sql
// Example SqlCommand, SqlDataAdapter
class DataBaseConnection
{
private OdbcConnection conn1 = new OdbcConnection(#"FILEDSN=C:/OTPub/Ot.dsn;" + "Uid=sa;" + "Pwd=otdata#123;"); //"DSN=Ot_DataODBC;" + "Uid=sa;" + "Pwd=otdata#123;"
//insert,update,delete
public int SetData(string query)
{
try
{
conn1.Open();
OdbcCommand command = new OdbcCommand(query, conn1);
int rs = command.ExecuteNonQuery();
conn1.Close();
return rs;
}
catch (Exception ex)
{
conn1.Close();
throw ex;
}
}
//select
public System.Data.DataTable GetData(string sql)
{
try
{
conn1.Open();
OdbcDataAdapter adpt = new OdbcDataAdapter(sql, conn1);
DataTable dt = new DataTable();
adpt.Fill(dt);
conn1.Close();
return dt;
}
catch (Exception ex)
{
conn1.Close();
throw ex;
}
}
}
in your form you can make object to that database connection class
DataBaseConnection db = new DataBaseConnection();
now you cal call get set with your get set method as following
string sqlSpecialHoliyday = "SELECT * FROM Holiday WHERE Date_Time='" + selectdate + "' AND IDH='50'";
DataTable dtAdditionalholily = db.GetData(sqlSpecialHoliyday);
AD you can Set Data Using Set method
string insertloginlog = "INSERT INTO Login_Log (Service_No, Machine_Name) VALUES ('" + serviceID + "','" + machiname + "')";
int ret = db.SetData(insertloginlog);
Hope This will help!
Related
This is my connection class
class connection
{
public SqlConnection con;
public SqlCommand cmd;
public SqlDataAdapter sda;
String pkk;
public void connectionFunc()
{
con = new SqlConnection(#"Data Source=.;Initial Catalog=payroll;Integrated Security=True");
con.Open();
}
public void dataSend(String SQL)
{
try
{
connectionFunc();
cmd = new SqlCommand(SQL, con);
cmd.ExecuteNonQuery();
pkk = "";
}
catch(Exception)
{
pkk = "error";
}
con.Close();
}
public void dataGet(String SQL)
{
try
{
connectionFunc();
sda = new SqlDataAdapter(SQL, con);
}
catch(Exception)
{
}
}
}
And this is the use of the class:
connection con = new connection();
con.dataGet("Select * from [users] Where Userame = '" + textBox1.Text + "' and Password = '" + textBox2.Text + "'");
DataTable dt = new DataTable();
// this line throws an error
con.sda.Fill(dt);
if(dt.Rows.Count > 0)
{
this.Hide();
Mainpage obj = new Mainpage();
obj.Show();
}
else
{
MessageBox.Show("Invalid UserName Or Password..!", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
I get the following error:
An unhandled exception of type 'System.NullReferenceException' occurred in Payroll_Manegement.exe
I don't know what is the issue here if someone could help me that would be great.
To answer your question: the error comes from the fact that your DataTable only is instantiated but not populated before the SqlDataAdapter.Fill is fired, so when the latter tries to do something useful it can only return an exception explaining exactly what is wrong.
Your connection class pains my eyes in terms of insecure variable scope (why use public everywhere?) and non-existent connection pooling. Using a class like this opens up your application for all kinds of insecure horror. Please try to keep things simple if your application is not too complicated; the basics from the .NET Framework should already be enough for your purposes. Just do something similar to the code below (with regard to parameterization of your queries like the others suggested):
string connectionString = "server=myServer;User ID=myUser;Password=myPwd;"; // could also be internal static on class level
string theQuery = "SELECT * FROM dbo.Users WHERE Username = #userName AND Password = #password";
using (SqlConnection sqlConnection = new SqlConnection(connectionString))
{
sqlConnection.Open();
using (SqlCommand sqlCommand = new SqlCommand(theQuery, sqlConnection))
{
sqlCommand.Parameters.AddWithValue("#userName", textBox1.Text);
sqlCommand.Parameters.AddWithValue("#password", textBox2.Text);
DataTable dataTable = new DataTable();
dataTable.Load(sqlCommand.ExecuteReader());
if (dataTable.Rows > 0)
{
SqlDataAdapter sqlDataAdapter = new SqlDataAdapter();
sqlDataAdapter.Fill(dataTable);
}
}
}
Alternatively, for the more complex applications you could study the Entity Framework https://learn.microsoft.com/en-us/ef/ef6/get-started first and train yourself in not re-inventing wheels. It will take some time to adapt but will really pay off.
Good luck improving your code!
I created the following code:
public static bool setHeadword(int id, string headword)
{
SqlConnection conn = new SqlConnection();
conn.ConnectionString = "Data Source=(LocalDB)\\MSSQLLocalDB;AttachDbFilename=|DataDirectory|\\pms.mdf;Integrated Security=True";
conn.Open();
SqlCommand command = new SqlCommand("UPDATE headwords SET Headword = #headword WHERE Id = #id", conn);
command.Parameters.AddWithValue("#headword", headword);
command.Parameters.AddWithValue("#id", id);
int result = command.ExecuteNonQuery();
conn.Close();
return true;
}
But the code doesn't work because the value in the database doesn't change.
If I run the code manually in the database the change takes place. But it won't work with C#.
Also the result variable are holding the right number of affected rows (1 in this case).
I'm not sure I have to flush the changes or something else.
Thanks for your help and best regards
Franz
static void Update(int id, string headword)
{
try
{
//You should create connectionString with correct details otherwise fail connection
string connectionString =
"server=.;" +
"initial catalog=employee;" +
"user id=sa;" +
"password=123";
using (SqlConnection conn =
new SqlConnection(connectionString))
{
conn.Open();
using (SqlCommand cmd =
new SqlCommand("UPDATE headwords SET Headword=#headword" +
" WHERE Id=#Id", conn))
{
cmd.Parameters.AddWithValue("#Id", id);
cmd.Parameters.AddWithValue("#headword", headword);
int rows = cmd.ExecuteNonQuery();
}
}
}
catch (SqlException ex)
{
//Handle sql Exception
}
}
Ok so i have a DBConnect class in which I have all my methods relating to the database of my program. I understand that connections are a relatively expensive resource so what is the correct way of opening and closing my database file.
This is what i'm currently using
I simply create the connection in the constructor and then open and close it when needed in the methods. Does this correctly return the connection to the pool? Also would it be better if I put a using(conn) in my methods.
public class DBConnect
{
SQLiteConnection conn = null;
public DBConnect()
{
string connStr = #"Data Source=tests.db; Version=3;";
conn = new SQLiteConnection(connStr);
}
public DBConnect(string connStr)
{
conn = new SQLiteConnection(connStr);
}
public DataTable DBSelect(string query)
{
/*
* Uses a select query to store data from the database into
* a datatable
*/
try
{
conn.Open();
DataTable dt = new DataTable();
using (SQLiteCommand cmd = new SQLiteCommand(query, conn))
{
using (SQLiteDataReader dr = cmd.ExecuteReader())
{
dt.Load(dr);
return dt;
}
}
}
catch (SQLiteException err)
{
MessageBox.Show("Caught exception: " + err.Message);
return null;
}
finally
{
conn.Close();
}
}//DBSelect
There is already an open DataReader associated with this Command which
must be closed first.
I m facing this issue when same person open the same page at same time on different system.
I have searched a lot on this but found no successful solution.
I have tired :
MultipleActiveResultSets = true in connection string
Increasing Connection waiting time
Verified all connection are closed
This issue comes only when above condition created. Kindly let me know solution which really works
this my connection function which i m using
public DataSet SelectDs(string str)
{
DataSet ds = new DataSet();
if (con.State == ConnectionState.Closed)
{
con.ConnectionString = ConStr;
con.Open();
}
cmd.CommandText = str;
cmd.Connection = con;
cmd.CommandTimeout = 12000;
adpt.SelectCommand = cmd;
adpt.Fill(ds);
con.Close();
return ds;
}
It is a mortal sin to use a global connection object in that way. It is bad (very bad) in WinForms applications, but in ASP.NET is deadly. (as you have discovered)
The usage pattern for a disposable object (and an expensive one like the connection) is
CREATE, OPEN, USE, CLOSE, DESTROY
The Connection Pooling mechanism exist to make easier the usage of this pattern.
Instead you try to work against it and you pay the consequences.
Your code should be rewritten as
public DataSet SelectDs(string str)
{
DataSet ds = new DataSet();
using(SqlConnection con = new SqlConnection(constring)) // CREATE
using(SqlCommand cmd = new SqlCommand(str, con)) // CREATE
{
con.Open(); // OPEN
cmd.CommandTimeout = 12000;
using(SqlAdapter adpt = new SqlAdapter(cmd)) // USE
adpt.Fill(ds);
return ds;
} // CLOSE & DESTROY
}
How about putting inside a Using statement like
using(SqlConnection connection = new SqlConnection("connection string"))
{
connection.Open();
using(SqlCommand cmd = new SqlCommand("SELECT * FROM SomeTable", connection))
{
using (SqlDataReader reader = cmd.ExecuteReader())
{
if (reader != null)
{
while (reader.Read())
{
//do something
}
}
} // reader closed and disposed up here
} // command disposed here
} //connection closed and disposed here
in finally clause use this
if (readerObj.IsClosed == false)
{
readerObj.Close();
}
I think you should also dispose your command object before returning dataset.
try cmd.Dispose() after con.close()
private static void ReadOrderData(string connectionString)
{
string queryString =
"SELECT OrderID, CustomerID FROM dbo.Orders;";
using (SqlConnection connection =
new SqlConnection(connectionString))
{
SqlCommand command =
new SqlCommand(queryString, connection);
connection.Open();
SqlDataReader reader = command.ExecuteReader();
// Call Read before accessing data.
while (reader.Read())
{
Console.WriteLine(String.Format("{0}, {1}",
reader[0], reader[1]));
}
// Call Close when done reading.
reader.Close();
}
}
How can I enhance the method above to accept any queryString? The problem is in the while. There's a fixed # of columns I can read. I want to be able to read any number of columns so that I can populate and return a DataSet. How can I do it?
You can do something along these lines:
private static void ReadOrderData(string connectionString,
string query, Action<SqlDataReader> action)
{
using (SqlConnection connection = new SqlConnection(connectionString))
{
SqlCommand command =
new SqlCommand(query, connection);
connection.Open();
SqlDataReader reader = command.ExecuteReader();
// Call Read before accessing data.
while (reader.Read())
{
action(reader);
}
// Call Close when done reading.
reader.Close();
}
}
You're really barking up the wrong tree. You shouldn't be using "methods that accept query strings". You should raise the level of abstraction by using Entity Framework or the like.
You will then not need to use the above code because it will not exist. Those who would have called that code will do something like this:
var orders = from o in ordersDAL.Orders
select new {o.OrderID, o.CustomerID};
foreach (var order in orders)
{
Console.WriteLine("{0}, {1}", order.OrderID, order.CustomerID);
}
Your code is badly designed in any case. Why in the world would you combine the fetching of the data with the use of it? That while loop should not be in that same method.
I would use something like the answer from obrok, but I would add the ability to use parameters.
Also, the SqlCommand and SqlDataReader both need to be within a using block:
private static void ReadOrderData(string connectionString,
string query, Action<SqlDataReader> action)
{
using (SqlConnection connection = new SqlConnection(connectionString))
{
using (SqlCommand command =
new SqlCommand(query, connection)) {
connection.Open();
using (SqlDataReader reader = command.ExecuteReader()) {
// Call Read before accessing data.
while (reader.Read())
{
action(reader);
}
// No need to call Close when done reading.
// reader.Close();
} // End SqlDataReader
} // End SqlCommand
}
}
Use SqlDataAdapter:
private static DataSet ReadData(string connectionString, string queryString)
{
DataSet dataSet;
using (SqlConnection connection =
new SqlConnection(connectionString))
{
connection.Open();
SqlCommand command =
new SqlCommand(queryString, connection);
SqlDataAdapter adapter = new SqlDataAdapter(command);
adapter.Fill(dataSet);
}
return dataSet;
}
Or something like this.
How I'm doing this:
(basically idea is to use DataSet along with IDataAdapter)
DataSet ds = null;
List<SqlParameter> spParams = ...
using (SqlConnection connection = new SqlConnection(ConnectionString))
{
using (SqlCommand command = new SqlCommand(spName, connection))
{
command.CommandType = CommandType.StoredProcedure;
command.Parameters.Clear();
command.Parameters.AddRange(spParams);
connection.Open();
IDbDataAdapter da = new SqlDataAdapter();
da.SelectCommand = command;
ds = new DataSet("rawData");
da.Fill(ds);
ds.Tables[0].TableName = "row";
foreach (DataColumn c in ds.Tables[0].Columns)
{
c.ColumnMapping = MappingType.Attribute;
}
}
}
// here is you have DataSet flled in by data so could delegate processing up to the particular DAL client
You said you tried EF, but did you try Dapper ? looks like a simple enough ORM that should work with your database (it's raw SQL), and will avoid you most of this mapping code. Dapper is used in StackOverflow so it cannot be too bad :)
Same suggestion as 'WorkerThread' but change the Method Signature to:
private static DataSet ReadOrderData(string connectionString, string queryString)
{
// do work
}
Drop the following line from 'WorkerThread' example:
string queryString = "SELECT OrderID, CustomerID FROM dbo.Orders;";
Once you have made these two changes to 'WorkerThread's' method it should be perfect for what you need.
Look at the DataTable.Load method. Or, for a finer level of control, check out the properties and methods on IDataReader, such as FieldCount, GetFieldType, and GetName.