I have designed my application in a layered approach. I have a BusinessOP layer for each interface and a common data access layer. In my Data access layer I have Data Reader method like this.|
public SqlDataReader executeQuerys(string query01)
{
SqlConnection con = null;
SqlCommand com = null;
try
{
con = new SqlConnection(DBConnect.makeConnection());
con.Open();
com = new SqlCommand(query01, con);
return com.ExecuteReader(CommandBehavior.CloseConnection);
}
catch
{
com.Dispose();
con.Close();
throw;
}
This is the code for my DBConnection layer.
public static string makeConnection()
{
string con = ConfigurationManager.ConnectionStrings["MyDB.Properties.Settings.ConString"].ToString();
return con;
}
In my business layer I have methods like this each calling a specific stored procedure.
public SqlDataReader getLGDivID(string divName)
{
string query = "EXEC getLGDivID'" + divName + "'";
return new DataAccessLayer().executeQuerys(query);
}
As my business operation layer is unsecure, I want to have it with parameterized query in here I'm using string concatenation to pass parameters. Can anyone hint me how to modify it?
You can change your function a little bit:
public SqlDataReader executeQuerys(string query01, string paramName, string value)
{
SqlConnection con = null;
SqlCommand com = null;
try
{
con = new SqlConnection(DBConnect.makeConnection());
con.Open();
com = new SqlCommand(query01, con);
com.Parameters.AddWithValue(paramName, value);
com.Dispose();
con.Close();
}
catch
{
com.Dispose();
con.Close();
throw;
}
return com.ExecuteReader(CommandBehavior.CloseConnection);
}
then to use it:
public SqlDataReader getLGDivID(string divName)
{
string query = "EXEC getLGDivID #divName";
return new DataAccessLayer().executeQuerys(query, "#divName", divName);
}
EDIT:
As #silvermind pointed out, you should dispose your connection properly.
The way you have it now it will dispose connection only when you catch an exception.
This is bad, make use of IDisposable, for example:
public SqlDataReader executeQuerys(string query01, string paramName, string value)
{
using (SqlConnection con = new SqlConnection(DBConnect.makeConnection()))
{
try
{
con.Open();
com = new SqlCommand(query01, con);
com.Parameters.AddWithValue(paramName, value);
}
catch(SqlException ex)
{
//Handle the exceptio
//no need to dispose connection manually
//using statement will take care of that
}
}
return com.ExecuteReader(CommandBehavior.CloseConnection);
}
Related
I placed my DB reader in a separate class file because I didn't want to keep rewriting it but I keep getting the error:
Object reference not set to an instance of an object. db was null
This is my DataReader:
namespace ProjectName
{
public class DBReader
{
string dsn = ConfigurationManager.ConnectionStrings["database"].ConnectionString.ToString();
public SqlDataReader SqlReader(string sql, string retDebug = "")
{
try
{
SqlConnection conn;
SqlCommand cmd;
SqlDataReader dr_user;
conn = new SqlConnection(dsn);
conn.Open();
try
{
cmd = new SqlCommand(sql, conn);
cmd.CommandType = CommandType.Text;
cmd.CommandTimeout = 180;
dr_user = cmd.ExecuteReader(CommandBehavior.CloseConnection);
return dr_user;
}
catch (SqlException ex)
{
retDebug = ex.Message;
return null;
}
conn.Close();
conn.Dispose();
}
catch (Exception ex)
{
retDebug = ex.Message;
}
return null;
}
}
}
This is where I'm catching the error...at
SqlDataReader reader = db.SqlReader(query, "");
in the code shown here:
<!DOCTYPE html>
<script runat="server">
ProjectName.DBReader db;
string projectName;
protected void Page_Load(object sender, EventArgs e)
{
LoadProjects();
}
public void LoadProjects()
{
string query = #"SELECT * FROM projects where project_type = 3;
SqlDataReader reader = db.SqlReader(query, "");
while (reader.Read())
{
//code does something here
}
}
</script>
I want to be able to reuse this because I know I will be using it many times in this project.
Any help/direction would be appreciated!
As others pointed out, like any class, you have to create a instance of that class before using.
HOWEVER, if you don't have any public vars (at least ones that will change with different users on the web site), then you can also have public members of that class, and create the class as static. (but, those public members MUST be the same for all logged on users)
So, your choice.
Always create an instance of the class before using.
eg this:
public void LoadProjects()
{
string query = #"SELECT * FROM projects where project_type = 3";
DBReader MyDB = new DBReader();
SqlDataReader reader = MyDB.SqlReader(query, "");
while (reader.Read())
{
//code does something here
}
}
Or, you can declare the class as static, like this:
(air code warning).
public static class DBReader
{
static readonly string dsn = ConfigurationManager.ConnectionStrings["database"].ConnectionString.ToString();
static public SqlDataReader SqlReader(string sql, string retDebug = "")
{
SqlDataReader dr_user = null;
using (SqlConnection conn = new SqlConnection(dsn))
{
using (SqlCommand cmd = new SqlCommand(sql, conn))
{
cmd.CommandType = CommandType.Text;
cmd.CommandTimeout = 180;
conn.Open();
dr_user = cmd.ExecuteReader();
}
}
return dr_user;
}
}
So, now in code you don't have to create a instance.
eg:
public void LoadProjects()
{
string query = #"SELECT * FROM projects where project_type = 3";
SqlDataReader reader = MyCode.DBReader.SqlReader(query, "");
while (reader.Read())
{
//code does something here
}
}
I have a Class that processes DB column metadata. One of the Properties of the Class is the table in question. This is passed to the object via the constructor. Also in the constructor I apply some logic to assign other variables within the class. To do this there are a number of private methods that connect to a DB, query something about the table, and return a value to the variable.
My problem is that I have a lot of different methods doing pretty much the same thing, but returning a different datatype. So for example my code is something like this
public Column(string tableName)
{
strTableName = tableName;
pkColumnName = GetPKColumnName(tableName);
pkColumnLenght = GetPKColumnLenght(tableName);
}
private string GetPKColumnName(string tableName)
{
string query = String.Format("SELECT myColName FROM myTable where myTableName = {0}", tableName);
string result = "";
try
{
using(SqlConnection con = new SqlConnection(ConfigurationManager.ConnectionStrings["MyDB"].ConnectionString))
{
con.Open();
using (SqlCommand command = new SqlCommand(query, con))
{
result = (string)command.ExecuteScalar();
}
}
}
catch (SqlException ex)
{
Console.WriteLine(ex.Message);
}
return result;
}
private int GetPKColumnLenght(string tableName)
{
string query = String.Format("SELECT myColLenght FROM myTable where myTableName = {0}", tableName);
int result = 0;
try
{
using(SqlConnection con = new SqlConnection(ConfigurationManager.ConnectionStrings["MyDB"].ConnectionString))
{
con.Open();
using (SqlCommand command = new SqlCommand(query, con))
{
result = (int)command.ExecuteScalar();
}
}
}
catch (SqlException ex)
{
Console.WriteLine(ex.Message);
}
return result;
}
There are many other methods like this also. This didn't look great to me, so I was wondering what the best practice was for something like this.
Should I just declare the return type as an object and do the datatype conversions when assigning the returned value to my variable?
My answer and the other assume different questions. It appears to me that you are trying to query a single value from a specific column and that you have to create a new method because the types are different. That said, I would personally just use a simple ORM solution and the other answer certainly is not wrong, just another abstraction.
You will want to use generics and cast to the generic.
I haven't tested this code, it is more of a guideline.
private T GetValue<T>(string tableName, colName)
{
string query = String.Format("SELECT {0} FROM myTable where myTableName = {1}", colName, tableName);
T result = default(T);
try
{
using(SqlConnection con = new SqlConnection(ConfigurationManager.ConnectionStrings["MyDB"].ConnectionString))
{
con.Open();
using (SqlCommand command = new SqlCommand(query, con))
{
result = (T)command.ExecuteScalar();
}
}
}
catch (SqlException ex)
{
Console.WriteLine(ex.Message);
}
return result;
}
Create SqlManager class
public class SqlManager
{
public static string ConnectionString
{
get
{
return "Your ConnectionString"
}
}
public static SqlConnection GetSqlConnection(SqlCommand cmd)
{
if (cmd.Connection == null)
{
SqlConnection conn = new SqlConnection(ConnectionString);
conn.Open();
cmd.Connection = conn;
return conn;
}
return cmd.Connection;
}
public static object ExecuteScalar(SqlCommand cmd)
{
SqlConnection conn = GetSqlConnection(cmd);
try
{
return cmd.ExecuteScalar();
}
catch
{
throw;
}
finally
{
conn.Close();
}
}
}
Now your methods, for the second one same thing:
private string GetPKColumnName(string tableName)
{
string query = String.Format("", tableName);
SqlCommand cmd = new SqlCommand();
cmd.CommandText = #"SELECT myColName FROM myTable where myTableName = #TableName";
cmd.Parameters.AddWithValue("#TableName", tableName);
object result = SqlManager.ExecuteScalar(cmd);
return result != null ? (int)object: 0;
}
I have a c# login forum that has two text-boxes
1.username
2.password
I am trying to check if the user exists in my Oracle database or not. If so, I want it to do something (like call another forum, etc...), but I'm getting an error msg that says I have a missing expression. Whats wrong with it?
private void button1_Click(object sender, EventArgs e)
{
isUserExist(textBox1.Text,textBox2.Text);
}
public bool isUserExist(string username,string password)
{
try
{
string connstring = "data source=test_db;user id=system;password=password;";
string statementcmd = "SELECT * FROM register_user Where UserName=#username";
OracleConnection conn = new OracleConnection(connstring);
OracleCommand cmd = new OracleCommand();
cmd.Connection = conn;
cmd.CommandText = statementcmd;
cmd.Parameters.Add("#username", username);
if (conn.State != ConnectionState.Open)
{
conn.Open();
OracleDataReader reader = cmd.ExecuteReader();
if (!reader.HasRows)
{ MessageBox.Show("User Name Not Found"); }
if (!password.Equals(reader["password"].ToString()))
MessageBox.Show("Incorrect Password");
reader.Close();
}
return true;
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
return false;
}
}
You need to call the Read method on the DataReader before trying to access the properties.
if (reader.Read())
{
// Do stuff
}
Good luck!
1. you need to assign parameters before assigning commandstring to the CommandText.
2. you need to call Read() OracleDataReader object reader before accessing the records.
3. you should return true when true only when user is found.(in second if condition open curly braces is missing).
4. you can use using{} block for all IDisposable Implemented classes in your program so that their objects disposal will be taken care.(so you don't need to call Close() on Connection or Command objects)
Complete Solution:
public bool isUserExist(string username,string password)
{
bool status=false;
try
{
string connstring = "data source=test_db;user id=system;password=password;";
string statementcmd = "SELECT * FROM register_user Where [UserName]=#username";
using(OracleConnection conn = new OracleConnection(connstring))
{
using(OracleCommand cmd = new OracleCommand())
{
cmd.Connection = conn;
cmd.Parameters.Add("#username", username);//add parameters before assigning it to CommandText
cmd.CommandText = statementcmd;
if (conn.State != ConnectionState.Open)
{
conn.Open();
OracleDataReader reader = cmd.ExecuteReader();
if (!reader.Read())
{ MessageBox.Show("User Name Not Found"); }
if (!password.Equals(reader["password"].ToString()))
{
status=true;
MessageBox.Show("Incorrect Password");
}
}
}
}
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
status=false;
}
return status;
}
I try to call function to select data from database,coz it will more efficient and i don't like to open connection and execute reader every time,have any solution can do like that?
this is my first method to select data from database,but will hit sql injection problem
protected void Button1_Click(object sender, EventArgs e)
{
Class1 myClass = new Class1();
lblAns.Text = myClass.getdata("Table1", "Student", "Student = '" + TextBox1.Text + "'");
}
public string getdata(string table,string field,string condition)
{
SqlDataReader rdr;
SqlConnection conn = new SqlConnection(#"Data Source=.\SQLEXPRESS;AttachDbFilename=|DataDirectory|\Database1.mdf;Integrated Security=True;User Instance=True");
string sql = "select " + field + " from " + table + " where " + condition;
try
{
conn.Open();
SqlCommand cmd = new SqlCommand(sql, conn);
rdr = cmd.ExecuteReader();
while (rdr.Read())
{
return "true";
}
}
catch (System.Data.SqlClient.SqlException ex)
{
string msg = "Insert Error:";
msg += ex.Message;
}
finally
{
conn.Close();
}
return "false";
}
this is my second method but will hit error (ExecuteReader requires an open and available Connection. The connection's current state is closed.) at line (rdr = cmd.ExecuteReader();)
public string getdata(SqlCommand command,SqlConnection conn)
{
SqlDataReader rdr;
try
{
conn.Open();
SqlCommand cmd = new SqlCommand();
cmd = command;
rdr = cmd.ExecuteReader();
while (rdr.Read())
{
return "true";
}
}
catch (System.Data.SqlClient.SqlException ex)
{
string msg = "Select Error:";
msg += ex.Message;
}
finally
{
conn.Close();
}
return "false";
}
public SqlConnection conn()
{
SqlConnection conn = new SqlConnection(#"Data Source=.\SQLEXPRESS;AttachDbFilename=|DataDirectory|\Database1.mdf;Integrated Security=True;User Instance=True");
return conn;
}
protected void Button1_Click(object sender, EventArgs e)
{
Class1 myClass = new Class1();
string strSql;
strSql = "Select student from Table1 where student=#stu";
SqlCommand command = new SqlCommand(strSql, myClass.conn());
command.Parameters.AddWithValue("#stu", TextBox1.Text);
myClass.getdata(command, myClass.conn());
}
have solution can use 1st method but will not hit the sql injection problem?
Use ALWAYS the second solution. The only way to avoid Sql Injection is through the use of parameterized queries.
Also fix the error on the second example. You don't associate the connection to the command, also it is a bad practice to keep a global object for the connection. In ADO.NET exist the concept of Connection Pooling that avoid the costly open/close of the connection while maintaining a safe Handling of these objects
public string getdata(SqlCommand command)
{
// Using statement to be sure to dispose the connection
using(SqlConnection conn = new SqlConnection(connectionString))
{
try
{
conn.Open();
cmd.Connection = conn;
SqlDataReader rdr = cmd.ExecuteReader();
while (rdr.Read())
{
return "true";
}
}
catch (System.Data.SqlClient.SqlException ex)
{
string msg = "Select Error:";
msg += ex.Message;
return msg;
}
}
return "false";
}
I am trying to get a better handle on decoupling my code, code reuse, etc.
I'm tired of typing the below every time I want to read some rows:
using(SqlConnection conn = new SqlConnection(myConnString))
{
using(SqlCommand cmd = new SqlCommand(cmdTxt, conn))
{
conn.Open();
using(SqlDataReader rdr = cmd.ExecuteReader())
{
while(rdr.Read())
{
/* do something with rows */
}
}
}
}
I understand there is LINQ to SQL (I don't like it), and the Entity Framework (still a baby). I have no problems having to type my queries out, I just don't want to have to type the command contruction, row iterator, etc each time.
I looked around and found something that I thought would work for me, and tried to implement it to make things easier for me. As you can see in the comment, I get an error that the SqlDataReader is closed. I'm guessing it's probably because of the using statement int the DataFactory.ExecuteReader() method. When the reader is returned, the dispose method is called on my SqlConnection and SqlCommand variables. Am I right there? If so, how should one manage the connection and command variables?
Edit: I updated my code example to better reflect what I am doing.
public class DataFactory
{
public DataFactory()
{}
public DataFactory(string connectionString)
{
_connectionString = connectionString;
}
protected _connectionString = "Data Source=Localhost, etc, etc";
private string ConnectionString
{
get{return _connectionString;}
}
public SqlConnection GetSqlConnection()
{
return new SqlConnection(ConnectionString);
}
public SqlDataReader ExecuteReader(string cmdTxt)
{
using(SqlConnection conn = new SqlConnection(ConnectionString))
{
using(SqlCommand cmd = new SqlCommand(cmdTxt, conn))
{
conn.Open();
return cmd.ExecuteReader();
}
}
}
}
public IRepository<T>
{
T GetById(int id);
}
public MyTypeRepository: IRepository<MyType>
{
private static DataFactory _df = new DataFactory();
public MyType GetById(int id)
{
string cmdTxt = String.Format("SELECT Name FROM MyTable WHERE ID = {0}", id);
using(SqlDataReader rdr = _df.ExecuteReader(cmdTxt))
{
if(rdr.Read()) /* I get an error that the reader is already closed here */
{
return new MyType(
Convert.ToInt32(rdr["Id"]),
rdr["Name"]);
}
else
{
return null;
}
}
}
}
public class MyType
{
public MyType(int id, string name)
{
_id = id;
_name = name;
}
private string _name;
public string Name
{
get{return _name;}
}
private int _id;
public int Id
{
get{return _id;}
}
public override void ToString()
{
return string.Format("Name: {0}, Id: {1}", Name, Id);
}
}
public class Program
{
private static MyTypeRepository _mtRepo = new MyTypeRepository();
static void Main()
{
MyType myType = _mtRepo.GetById(1);
Console.WriteLine(myType.ToString());
}
}
I also would like to know if what I'm doing makes any sense, or, if not, how to achieve something similar so that I don't have to type the connection creation, etc so often.
Your method ExecuteReader will close the connection before returning the Reader. Instead it should be implemented something like:
public IDataReader ExecuteReader(string cmdTxt)
{
SqlConnection conn = new SqlConnection(...);
try
{
SqlCommand cmd = new SqlCommand(cmdTxt, conn);
conn.Open();
return cmd.ExecuteReader(CommandBehavior.CloseConnection);
}
catch
{
conn.Close();
throw;
}
}
Callers of the ExecuteReader method will need to dispose the IDataReader:
using(IDataReader reader = ExecuteReader(commandText))
{
...
} // reader will be disposed here and will close the connection.
Note that the above does not call Dispose on the SqlCommand object. In my experience and from looking at SqlCommand with Reflector it's not necessary as long as the SqlConnection is disposed. But I believe the following will work if you do want to dispose it:
public IDataReader ExecuteReader(string cmdTxt)
{
SqlConnection conn = new SqlConnection(...);
SqlCommand cmd = null;
try
{
cmd = new SqlCommand(cmdTxt, conn);
conn.Open();
IDataReader reader =
cmd.ExecuteReader(CommandBehavior.CloseConnection);
cmd.Dispose();
return reader;
}
catch
{
if (cmd != null) cmd.Dispose();
conn.Close();
throw;
}
}
It's very important that you close and/or dispose your data reader after using it then everyone who wants to use your DataFactory should remember to do that.I think it's a good idea to return a DataTable instead of SqlDataReader so that your DataFactory is not dependent to SqlDataReader.
I mean :
public DataTable ExecuteReader(string cmdTxt)
{
using(SqlConnection conn = new SqlConnection(ConnectionString))
{
using(SqlCommand cmd = new SqlCommand(cmdTxt, conn))
{
conn.Open();
using(SqlDataReader reader=cmd.ExecuteReader())
{
DataTable dt=new DataTable();
dt.Load(reader);
return dt;
}
}
}
}
EDIT:
Good point.I don't like data tables either ( We use NHibernate so I actually don't use data tables in our applications)
So if you'd like to map a data reader to your own objects maybe you can have a data mapper that maps data reader to your own objects I mean:
public T[] ExecuteReader<T>(string cmdTxt)
{
using(SqlConnection conn = new SqlConnection(ConnectionString))
{
using(SqlCommand cmd = new SqlCommand(cmdTxt, conn))
{
conn.Open();
using(SqlDataReader reader=cmd.ExecuteReader())
{
var result=new List<T>();
while(reader.Read())
result.Add(ObjectMapper.MapReader<T>(reader));
return result.ToArray();
}
}
}
}
What I do is I create an XML file with my queries and use an XSLT transformation to generate my DAL code CS files. You can go as fancy as you like, declare parameters in the XML and generate methods with appropriate signatures in the XSLT etc etc. I have a blog entry that covers, for a related topic, how to integrate the XSLT transformation into your Visual Studio project. Now one may argue that using a typed dataset is the same thing and is a free lunch, but in my case I use asynchronous DAL based on BeginExecute/EndExecute. None of the VS tools gets this approach right so I basically had to build my own.
I would say it's not really decoupling enough - basically any module you have with "using System.Data.SqlClient" is coupled to your database. The whole point of a DAL is that the application is coupled to the DAL and the DAL is coupled to the database.