Proper parameterized query class for asp.net - c# application - sql server - c#

I am starting to encapsulate my queries in a single parameterized query class
Now can you please evaluate my classes for performance security and every other aspect possible ?
Do you have any suggestions ?
Here my select class
public static DataSet cmd_SelectQuery(string srCommandText, List<string> lstParameterNames, List<string> lstParameters)
{
DataSet dsCmdPara = new DataSet();
try
{
using (SqlConnection connection = new SqlConnection(DbConnection.srConnectionString))
{
using (SqlCommand cmd = new SqlCommand(srCommandText, connection))
{
cmd.CommandType = CommandType.Text;
for (int i = 0; i < lstParameterNames.Count; i++)
{
cmd.Parameters.AddWithValue(lstParameterNames[i], lstParameters[i]);
}
connection.Open();
using (SqlDataAdapter sqlDa = new SqlDataAdapter(cmd))
{
sqlDa.Fill(dsCmdPara);
return dsCmdPara;
}
}
}
}
catch (Exception E)
{
csPublicFunctions.insertIntoTblSqlErrors(srCommandText + " " + E.Message.ToString());
}
return dsCmdPara;
}
And here my update,delete class
public static void cmd_UpdateDeleteQuery(string srCommandText, List<string> lstParameterNames, List<string> lstParameters)
{
try
{
using (SqlConnection connection = new SqlConnection(DbConnection.srConnectionString))
{
using (SqlCommand cmd = new SqlCommand(srCommandText, connection))
{
cmd.CommandType = CommandType.Text;
for (int i = 0; i < lstParameterNames.Count; i++)
{
cmd.Parameters.AddWithValue(lstParameterNames[i], lstParameters[i]);
}
connection.Open();
cmd.ExecuteNonQuery();
}
}
}
catch (Exception E)
{
csPublicFunctions.insertIntoTblSqlErrors(srCommandText + " " + E.Message.ToString());
}
}
Thanks a lot for answers

Two quick suggestions since I'm writing this on a phone.
Take in objects, not string
Taking in params object[] as the parameter list might make your methods a little more natural to use.

The code is valid and correct as it stands. You asked for feedback, so I have the following points.
(1) In the code where you are building up the parameters, it's a good idea to specify the data type.
for (int i = 0; i < lstParameterNames.Count; i++)
{
var parameter = cmd.Parameters.AddWithValue(lstParameterNames[i], lstParameters[i]);
parameter.SqlDbType = SqlDbType.NVarChar; // or whatever type you need
}
(2) You don't need to explicitly open/close the connection, as the Fill method does that for you.
connection.Open();
using (SqlDataAdapter sqlDa = new SqlDataAdapter(cmd))
{
sqlDa.Fill(dsCmdPara);
connection.Close();
return dsCmdPara;
}
(3) If you are only going to have one table, use a DataTable instead of a DataSet. It will be faster and less complex.
(4) This is more of a style point, but I find it is very helpful to explicitly close your connections. If there is an exception in the closing, having the line number (via the pdb) is very helpful. Also, it helps make sure you don't leak connections if you forget a using.
Edit
I want to clarify point 4. Assume that the when a connection in closed, the cleanup caused an exception. If you don't explicitly close the connection, the exception would be raised in the Dispose method, which might be doing a bunch of other work. This makes it harder to see what I did to cause the problem.
Take this simple example. There is an underlying resource that needs to be cleaned up (the entire reason for Dispose), and it throws an exception.
public class Resource: IDisposable
{
public void Close()
{
CleanupMemory();
}
private void CleanupMemory()
{
throw new Exception();
}
public void Dispose()
{
CleanupMemory();
}
}
The (cleaned up) stack trace for the two different approaches are:
using (var r = new Resource())
{
r.Close();
}
at ConsoleApplication1.Resource.CleanupMemory() in Program.cs:line 31
at ConsoleApplication1.Resource.Close() in Program.cs:line 26
at ConsoleApplication1.Program.Main(String[] args) Program.cs:line 16
versus
using (var r = new Resource())
{
}
at ConsoleApplication1.Resource.CleanupMemory() in Program.cs:line 35
at ConsoleApplication1.Resource.Dispose() in Program.cs:line 40
at ConsoleApplication1.Program.Main(String[] args) in Program.cs:line 18
In the first example, I know that my calling Close caused the exception, I have a place to start debugging. If I only know that the exception was thrown, I need to completely rely on the information in the exception. I have no other context. And most exception messages are not especially clear. :)
Both are totally correct, just different.
Erick

public DataTable Get_DTable(String Query, Dictionary<String, String> Parameters)
{
try
{
using (con = new SqlConnection(cls_Connection.URL()))
{
if (con.State == 0)
con.Open();
using (cmd = new SqlCommand(Query, con))
{
foreach (KeyValuePair<string, string> item in Parameters)
{
cmd.Parameters.AddWithValue(item.Key, item.Value);
}
using (da = new SqlDataAdapter(cmd))
{
using (dt = new DataTable())
{
da.Fill(dt);
if (dt.Rows.Count > 0)
{
return dt;
}
}
}
}
}
}
catch (Exception exp)
{
MessageBox.Show(exp.Message,
"Information",
MessageBoxButtons.OK,
MessageBoxIcon.Information);
}
return null;
}

Related

Returning MySqlDataReader

I want to return the value so on the caller i can use for example asd["columnname"] but im getting one error, example/code below.
I have this code
public static MySqlDataReader QueryResultadoMultString(string Query)
{
using (var conn = new MySqlConnection(myConnectionString))
{
try
{
conn.Open();
var cmd = conn.CreateCommand();
cmd.CommandText = Query;
MySqlDataReader myReader = null;
myReader = cmd.ExecuteReader();
if (myReader.HasRows)
{
while (myReader.Read())
{
Console.WriteLine(myReader.GetString(0));
return myReader;
}
}
return myReader;
}
catch (MySqlException ex)
{
NAPI.Util.ConsoleOutput($"[BaseDados][Erro] {ex.Message}");
return null;
}
}
}
Below is the caller
var asd = BaseDadosSQL.QueryResultadoMultString($"SELECT `socialclub`,`username`,`password` FROM contas WHERE socialclub = '{player.SocialClubName}'");
Console.WriteLine("Result "+asd["username"]);
I'm getting this error
System.Exception: 'No current query in data reader'
The method as written will force you to write code that is horribly vulnerable to sql injection issues. You need a separate set of arguments for parameters.
You want something more like this (which should also fix the issue in your question):
public static class BaseDadosSQL
{
private static string connectionString = "connection string here";
public static IEnumerable<IDataRecord> QueryResult(string Query, params MySqlParameter[] parameters)
{
using (var conn = new MySqlConnection(connectionString))
using (var cmd = new MySqlCommand(Query, conn))
{
if (parameters is object && parameters.Length > 0)
{
cmd.Parameters.AddRange(parameters);
}
conn.Open();
using (var reader = cmd.ExecuteReader())
{
while (reader.Read())
{
yield return reader;
}
}
}
}
}
And then call it like this:
//Guessing at type and length here. Use the actual type and length from the database
var p = new MySqlParameter("#SocialClub", MySqlDbType.VarString, 20);
p.Value = player.SocialClubName;
try
{
var asd = BaseDadosSQL.QueryResult($"SELECT `socialclub`,`username`,`password` FROM contas WHERE socialclub = #SocialClub", p);
foreach(var result in asd)
{
Console.WriteLine("Result " + result["username"]);
}
}
catch (MySqlException ex)
{
NAPI.Util.ConsoleOutput($"[BaseDados][Erro] {ex.Message}");
}
This code will let you use Social Club names which includes apostrophes. The original would have blown up. Notice I also moved the exception handling out of the DB code.
Ideally, even the QueryResult() method should also be private, with the BaseDadosSQL class having a separate public method for each query you need to run. So it would look more like this:
public static class BaseDadosSQL
{
private static string connectionString = "connection string here";
private static IEnumerable<IDataRecord> QueryResult(string Query, params MySqlParameter[] parameters)
{
using (var conn = new MySqlConnection(connectionString))
using (var cmd = new MySqlCommand(Query, conn))
{
if (parameters is object && parameters.Length > 0)
{
cmd.Parameters.AddRange(parameters);
}
conn.Open();
using (var reader = cmd.ExecuteReader())
{
while (reader.Read())
{
yield return reader;
}
}
}
}
public static IEnumerable<IDataRecord> GetClubLogin(string clubName)
{
//Still guessing at type and length here.
var p = new MySqlParameter("#SocialClub", MySqlDbType.VarString, 20);
p.Value = clubName;
return QueryResult($"SELECT `socialclub`,`username`,`password` FROM contas WHERE socialclub = #SocialClub", p);
}
}
And then called like this:
try
{
foreach(var result in BaseDadosSQL.GetClubLogin(player.SocialClubName))
{
Console.WriteLine("Result " + result["username"]);
}
}
catch (MySqlException ex)
{
NAPI.Util.ConsoleOutput($"[BaseDados][Erro] {ex.Message}");
}
Finally, it's really Really REALLY BAD to store passwords like that. So bad, it's not even okay for testing/learning/proof of concept code. NEVER DO THAT! It's not even okay to store passwords encrypted. Encryption is not good enough.
Passwords should only ever be stored as a fixed-length, salted, cryptographic (not-MD5) hash value. When someone tries to login, you salt and hash the attempted credential, and then compare the hash values, not the actual password. Anything else is just begging to end up on the front page of your newspaper of choice as the latest big data breach.

Get Data from stored procedure as Datatable

I have a method to retrieve data from a database using a stored procedure as a DataTable like:
public DataTable GetTableBySQL(string sql)
{
SqlCommand cmd = new SqlCommand(sql.ToString(), this.dbconn)
{
CommandTimeout = 0,
CommandType = CommandType.Text
};
DataTable tbl = new DataTable("Table1")
{
Locale = System.Globalization.CultureInfo.InvariantCulture
};
SqlDataAdapter da = new SqlDataAdapter(cmd);
try
{
da.SelectCommand.CommandTimeout = 0;
da.Fill(tbl);
}
catch (SqlException e)
{
this.HandleSQLError(e, "GetTableBySQL", sql.ToString());
}
finally
{
cmd.Dispose();
da.Dispose();
}
return tbl;
}
Now I call the stored procedure like this:
var empList = db.GetTableBySQL("$exec getMySP");
But when I execute, it just don't return any columns.
What am I doing wrong? Regards
There are three main problems here (other smaller ones, but three that are important):
The $exec part of the SQL doesn't mean anything. Maybe you just want exec.
When the bad SQL fails, the error is hidden from the program, so you don't really know what happened.
The method signature doesn't support query parameters, and therefore will force you to write horribly insecure code that will result in someone hacking your application. Probably sooner rather than later. This is really bad, and you should not ignore it.
Try something more like this:
public DataTable GetTableBySQL(string sql, params SqlParameter[] parameters)
{
var result = new DataTable();
//ADO.Net really does work better when you create a **NEW** connection
// object for most queries. Just share the connection string.
//Also: "using" blocks are a better way to make sure the connection is closed.
using (var dbconn = new SqlConnection(this.dbConnectionString))
using (var cmd = new SqlCommand(sql, dbconn))
using (var da = new SqlDataAdapter(cmd))
{
cmd.CommandTimeout = 0;
// A number of the properties set on the cmd and tbl variables just set the same value that was already there, didn't accomplish anything
//It's hard to understate how important it is to use parameterized queries.
if (parameters != null && parameters.Length > 0)
{
cmd.Parameters.AddRange(parameters);
}
try
{
da.Fill(result);
}
catch (SqlException e)
{
this.HandleSQLError(e, "GetTableBySQL", sql.ToString());
//you may want to re-throw here,
// or even just remove the try/catch and let the error bubble up to calling code
}
}
return result;
}
Here it is again without all the extra explanatory comments, so you can see that doing it right is less code, rather than more:
public DataTable GetTableBySQL(string sql, params SqlParameter[] parameters)
{
var result = new DataTable();
using (var dbconn = new SqlConnection(this.dbConnectionString))
using (var cmd = new SqlCommand(sql, dbconn))
using (var da = new SqlDataAdapter(cmd))
{
cmd.CommandTimeout = 0;
if (parameters != null && parameters.Length > 0)
{
cmd.Parameters.AddRange(parameters);
}
da.Fill(result);
}
return result;
}
Then call it like this:
var empList = db.GetTableBySQL("exec getMySP");

Passing Connection From TableAdapter Produces connectionstring property has not been initialized

I ran into a problem where passing a connection from a TableAdapter to some methods throws an exception stating the connectionstring isn't initialized. There are quite a few questions on SO with this exception but none were passing the connection and most were because the ConnectionString was null. Weird thing is I used MessageBox.Show(connection.ConnectionString); through out the chain of methods and I receive a valid connection string at every step. This is a somewhat complicated program that is in production but I will try to simplify the code for this question...
This is the postInventoryData method, which takes a DataGridView with inventory items and iterates through it posting them to the inventory. I use a TransactionScope to ensure the changes are safely rolled back in the event of an error. If an item is a kit(an item comprised of other items) I must iterate through those items and remove them from the inventory. The problem occurs when I check whether or not the item is a kit.
public bool postInventoryData(DataGridView dgv)
{
bool successful = true;
TestDataSetTableAdapters.inentoryTrxTableAdapter inventoryTrxAdapter =
new TestDataSetTableAdapters.inentoryTrxTableAdapter();
try
{
using (TransactionScope trxScope = new TransactionScope
(TransactionScopeOption.Required, new System.TimeSpan(0, 15, 0)))
{
MessageBox.Show(inventoryTrxAdapter.Connection.ConnectionString); // <-- Valid ConnectionString
inventoryTrxAdapter.OpenConnection();
for (int i = 0; i < dgv.Rows.Count; i++)
{
//parameter values
string departmentCode = dgv.Rows[i].Cells["Department_Code"].Value.ToString();
string machineCode = dgv.Rows[i].Cells["Machine_Code"].Value.ToString();
string operatorCode = dgv.Rows[i].Cells["Operator_Code"].Value.ToString();
string itemNumber = dgv.Rows[i].Cells["Item_Number"].Value.ToString();
double? qtyProduced = Convert.ToDouble(dgv.Rows[i].Cells["Quantity"].Value.ToString());
bool isKit =
businessLayer.isItemNumberKit
(inventoryTrxAdapter.Connection, itemNumber); // <-- CULPRIT!
// Inserts the item
dailyProductionInsertQty(
departmentCode,
machineCode,
operatorCode,
itemNumber,
isKit,
qtyProduced,
inventoryTrxAdapter,
trxScope);
}
inventoryTrxAdapter.CloseConnection();
trxScope.Complete();
}
}
catch (System.Exception ex)
{
successful = false;
MessageBox.Show(ex.ToString());
}
return successful;
}
The isItemNumberKit method
public bool isItemNumberKit(SqlConnection connection, string itemNumber)
{
bool contains;
MessageBox.Show(connection.ConnectionString); // <-- Valid ConnectionString
DataTable dt = getKit(connection, itemNumber); // <-- CULPRIT!
if (dt.Rows.Count > 0)
{
contains = true;
}
else
{
contains = false;
}
return contains;
}
The getKit method
public DataTable getKit(SqlConnection connection, string itemNumber)
{
DataTable dt = new DataTable();
SqlConnection myConnection = connection;
MessageBox.Show(myConnection.ConnectionString); // <-- Valid ConnectionString
SqlParameter paramItemNumber = new SqlParameter();
paramItemNumber.ParameterName = "#ItemNumber";
paramItemNumber.Value = itemNumber;
paramItemNumber.SqlDbType = System.Data.SqlDbType.VarChar;
try
{
using (myConnection)
{
string sql =
#"SELECT kits.Row_Id,
kits.Kit_Item_Number,
kits.Location_Code
FROM Inventory.dbo.Z_PV_Kits kits
WHERE kits.Kit_Item_Number=#ItemNumber";
//myConnection.Open();
using (SqlCommand myCommand = new SqlCommand(sql, myConnection))
{
myCommand.Parameters.Add(paramItemNumber);
SqlDataReader reader = myCommand.ExecuteReader();
dt.Load(reader);
}
}
}
catch (Exception ex)
{
dt = null;
MessageBox.Show(ex.ToString());
}
return dt;
}
When I execute postInventoryData the program throws an exception with the message, "The connectionstring property has not been initialized." with the line numbers pointing to isItemNumberKit and getKit. As you can see in the code above, I used a MessageBox.Show(connection.ConnectionString) throughout the process and each time I received a valid Connection string. I have created a workaround which stores a cached DataTable containing all the kit items I can run linq statements on. I am not in emergency mode or anything but I thought this to be weird and an opportunity for me to learn. Thanks in advance for any help!
It might be possible that you have 2 app.config files in your solution with 2 different connection strings.
OK, I figured it out and now when I think about it the answer was somewhat obvious. I always use using(){} blocks to ensure connections and similar objects are properly disposed and taken care of after they are used. The solution was to simply remove the using(myConnection){} block from the getKit method like this:
public DataTable getKit(SqlConnection connection, string itemNumber)
{
DataTable dt = new DataTable();
SqlConnection myConnection = connection;
MessageBox.Show(myConnection.ConnectionString);
SqlParameter paramItemNumber = new SqlParameter();
paramItemNumber.ParameterName = "#ItemNumber";
paramItemNumber.Value = itemNumber;
paramItemNumber.SqlDbType = System.Data.SqlDbType.VarChar;
try
{
string sql =
#"SELECT kits.Row_Id,
kits.Kit_Item_Number,
kits.Location_Code
FROM Inventory.dbo.Z_PV_Kits kits
WHERE kits.Kit_Item_Number=#ItemNumber
";
//myConnection.Open();
using (SqlCommand myCommand = new SqlCommand(sql, myConnection))
{
myCommand.Parameters.Add(paramItemNumber);
SqlDataReader reader = myCommand.ExecuteReader();
dt.Load(reader);
}
}
catch (Exception ex)
{
dt = null;
MessageBox.Show(ex.ToString());
}
return dt;
}
This will leave the connection intact but properly dispose of the command. Sorry for the long winded question with a short simple answer. Hope this might help someone someday.

DB Connection issue only some times

I have a problem with the below code,There's no any coding error BUT sometimes it throws some exceptions.I just wanted to know any code organizing issue ? & how to fix it.
Sometimes it shows those exceptions
1.ExecuteReader requires an open and available Connection. The connection's current state is closed.
2.Invalid attempt to call FieldCount when reader is closed.
But Sometimes it works without any issue,as expected
My Coding Goes here
[WebMethod, ScriptMethod]
public static List<HomeImageSliders> GetHomeImageSliders()
{
List<HomeImageSliders> HomeImageList = new List<HomeImageSliders>();
try
{
SqlCommand comHomeImage = new SqlCommand("SP_GetHomeImageSliders", conDB);
comHomeImage.CommandType = CommandType.StoredProcedure;
if (conDB.State != ConnectionState.Open)
{
conDB.Open();
}
SqlDataReader rdr = comHomeImage.ExecuteReader();
DataTable dt = new DataTable();
dt.Load(rdr);
foreach (DataRow r in dt.Rows)
{
HomeImageList.Add(new HomeImageSliders
{
Id = (int)r["Id"],
ImagePath = r["ImagePath"].ToString(),
ModifiedDate = Convert.ToDateTime(r["ModifiedDate"]).Date
});
}
}
catch (Exception ee)
{
}
finally
{
conDB.Close();
}
return HomeImageList;
}
You should use the "using" construction:
(using ommand comHomeImage = new SqlCommand("SP_GetHomeImageSliders", conDB) {
(using SqlDataReader rdr = new SqlDataReader) {
//do some things
}
}
I don't know why your connection sometimes is closed when you call
ExecuteReader()
But why don't you use a using block instead like this:
using(SqlConnection conDB = new SqlConnection(connectionString))
{
...
}
this will close your connection to the DB when it loses scope.
try to prevent using the same connection with two or more threads by usinglock 
lock(_conDb)
{
//// your code here
}
and also wrap your conDb in using block as below
using(SqlConnection conDB = new SqlConnection(connectionString))
{
...
}

Getting a connection to database lost error

every once and a while my application throws a connection to database lost error.
The database class I got from a tutorial site and is below, it works great except for the above error sometimes, im guessing its timing out, like if the person using it goes for a smoke break and comes back and tries to continue where they left off.
And of coarse being the normal end-user they close then error message THEN come get me to tell me they got an error.
But until the error comes up again i thought i would ask what part of this code could be changed to prevent that error
this is a firebird db server and a c# application
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
using System.Windows.Forms;
using FirebirdSql.Data.FirebirdClient;
namespace _0912111
{
class DatabaseConnection
{
private FbConnection conn;
private FbCommand sqlCommand;
private FbDataAdapter DB;
private DataSet DS = new DataSet();
public DatabaseConnection()
{
conn = new FbConnection("User=myuser;" + "Password=mypw;" + "Database=dbpath;" + "DataSource=serverip;" + "Port=dbport;" + "Dialect=3;" + "Charset=UTF8;");
}
public void showDbError(string theError)
{
MessageBox.Show("Could not connect to database\n\nError Details:\n" + theError);
}
public FbConnection Openconn()
{
if (conn.State == ConnectionState.Closed || conn.State == ConnectionState.Broken)
{
try
{
conn.Open();
}
catch (Exception e)
{
showDbError(e.Message.ToString());
}
}
return conn;
}
public FbConnection Closeconn()
{
if (conn.State == ConnectionState.Open)
{
conn.Close();
}
return conn;
}
public void nonQuery(string txtQuery)
{
FbCommand cmd = new FbCommand(txtQuery);
try
{
cmd.Connection = Openconn();
cmd.ExecuteNonQuery();
}
catch (Exception Ex)
{
showDbError(Ex.Message.ToString());
throw Ex;
}
finally
{
cmd = null;
}
}
public FbDataReader returnDataReader(string txtQuery)
{
FbCommand cmd = new FbCommand();
try
{
cmd.Connection = Openconn();
cmd.CommandText = txtQuery;
FbDataReader rd;
rd = cmd.ExecuteReader();
return rd;
}
catch (Exception Ex)
{
showDbError(Ex.Message.ToString());
throw Ex;
}
finally
{
cmd = null;
}
}
}
}
I would think that the code in her that says
if (conn.State == ConnectionState.Closed || conn.State == ConnectionState.Broken)
{
would prevent it??
Look, I suggest you to use using and rewrite your methods in a cleaner way, no reason to have another method to open and another to close connection, I'm not doing it anymore since longer than 5 years :D
also, no reason to do a ToString() on Ex.Message and also, notice, in C# you should throw exceptions with only throw not throw exc.
one of your methods would become this for example:
public void nonQuery(string txtQuery)
{
using(var conn = new FbConnection(GetMyConnectionString(...parameters...)))
{
using(var cmd = new FbCommand(txtQuery))
{
try
{
cmd.Connection = conn;
conn.Open();
cmd.ExecuteNonQuery();
}
catch (Exception ex)
{
showDbError(ex.Message);
throw;
}
}
}
}
I'm 100% with Davide Piras on this. (upvoted him)
Delete the "Openconn" and "Closeconn" methods from your DatabaseConnection class. Then change your queries to have using statements for the connection open and command execution.
The database drivers already know how to perform connection pooling. Maintaining an open connection in code is not just a waste of time, but a potential cause of issues like the one you are experiencing. Other issues it can cause are leaked memory and the ability to open further connections with the database server.
So, rewrite your code to use best practices for database access and the problem will go away.
I'll leave this example that speaks of the connection and some examples that I hope will help.
http://code.msdn.microsoft.com/Esempio-applicazione-dati-494c129a
Regards.

Categories

Resources