Returning MySqlDataReader - c#

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.

Related

How to get data in INT from SQLDataReader in .NET

I am trying to get int value from the database but It is throwing an error
Unable to cast object of type 'System.Byte' to type 'System.Int32'.
In the database, Active field is tinyint.
Also, how to return both values from this method.
private string CheckData(string firstValue, string SecondValue, int Active)
{
string Data = "";
StringBuilder sb = new StringBuilder();
string query = #"select M.ident Mi, mmp.active Active
from Iden.Iden M
inner join PtM.MPt MMP on MMP.mPat = M.id
where M.ident = 'firstValue'
and Mi.ident = 'SecondValue'";
sb.Append(query);
sb.Replace("firstValue", firstValue);
sb.Replace("SecondValue", SecondValue);
SqlConnection connection = new SqlConnection(connString);
SqlCommand cmd = new SqlCommand(sb.ToString());
cmd.CommandTimeout = 0;
cmd.CommandType = CommandType.Text;
cmd.Connection = connection;
try
{
connection.Open();
SqlDataReader reader = cmd.ExecuteReader();
if (reader.HasRows)
{
while (reader.Read())
{
Data = reader.GetString(reader.GetOrdinal("Mi"));
Active = reader.GetInt32(reader.GetOrdinal("Active"));
}
}
}
catch (Exception ex)
{
_log.Error($"Exception:{ex.Message}");
}
finally
{
connection.Close();
connection.Dispose();
}
return Data;
}
Here's a stab at it. I can't debug it (since I don't feel like creating a database).
First I create a type to hold the results. You could just use a Tuple, but this seems clearer:
public class DataActive
{
public string Data { get; set; }
public byte Active { get; set; }
}
I make your function return a collection of these - it's not obvious from your code that there is only one.
You'll also notice that I use SqlParameters to add firstValue and secondValue to your query. Look up SQL Injection (and Little Bobby Tables).
If you are using a recent version of C# (which I don't), there's a new syntax for using that requires less indenting. The using statements stick a call to Dispose in a finally statement at the end of the block. Also note that I'm disposing the SqlCommand and the DataReader
public static IEnumerable<DataActive> CheckData(string firstValue, string secondValue)
{
var results = new List<DataActive>();
const string query = #"select M.ident Mi,mmp.active Active from Iden.Iden M
Inner join PtM.MPt MMP on MMP.mPat =M.id
where M.ident = #firstValue and Mi.ident = #secondValue";
using (var connection = new SqlConnection(connString))
{
using (var cmd = new SqlCommand(query))
{
cmd.CommandTimeout = 0;
cmd.CommandType = CommandType.Text;
cmd.Connection = connection;
cmd.Parameters.Add("#firstValue", SqlDbType.NVarChar, 50).Value = firstValue;
cmd.Parameters.Add("#secondValue", SqlDbType.NVarChar, 50).Value = secondValue;
try
{
connection.Open();
using (var reader = cmd.ExecuteReader())
{
var dataOrdinal = reader.GetOrdinal("Mi");
var activeOrdinal = reader.GetOrdinal("Active");
if (reader.HasRows)
{
while (reader.Read())
{
results.Add(new DataActive
{
Data = reader.GetString(dataOrdinal),
Active = reader.GetByte(activeOrdinal),
});
}
}
}
}
catch (Exception ex)
{
_log.Error($"Exception:{ex.Message}");
}
}
}
return results;
}
If your TINY_INT Active represents a boolean value, figure out what the rule is, and do a conversion after you get the value using reader.GetByte.
One final note, it's often better to log ex.ToString() rather than ex.Message. You get the message and the stack that way.

Best way to create new SqlConnection when is null

I have two methods that connect to the database and I try to avoid double code
one of my methods is one that can run alon (open itself SqlConnection and close it)
another method using existing SqlConnection and using SqlTransaction also (I don't want to open another connection and also I don't want to close it)
my first method :
public static List<CSerieses> GetCSerieses(DeliveryReportObject DeliveryReportObject)
{
List<CSerieses> CSerieses = new List<CSerieses>();
try
{
using (SqlConnection openCon = new SqlConnection(connectionString))
{
string query = "SELECT [CSeriesNum],[CCount],[Mark] from [Serieses] " +
"where [TestPrimary]=#deliveryNumber";
SqlCommand command = new SqlCommand(query, openCon);
command.Parameters.AddWithValue("#deliveryNumber", DeliveryReportObject.DeliveryNumber);
openCon.Open();
using (SqlDataReader reader = command.ExecuteReader())
{
while (reader.Read())
{
CSerieses.Add(new CSerieses(reader.GetString(0), reader.GetInt32(1), reader.GetBoolean(2)));
}
}
openCon.Close();
}
}
catch (Exception ex)
{
LocalPulserDBManagerInstance.WriteLog(ex.StackTrace, ex.Message);
}
return CSerieses;
}
The method that using on the transaction :
public static List<CSerieses> GetCSerieses(DeliveryReportObject DeliveryReportObject,
SqlConnection co,SqlTransaction tran)
{
List<CSerieses> CSerieses = new List<CSerieses>();
try
{
using (co)
{
string query = "SELECT [CSeriesNum],[CCount],[Mark] from [Serieses] " +
"where [TestPrimary]=#deliveryNumber";
SqlCommand command = new SqlCommand(query, co, tran);
command.Parameters.AddWithValue("#deliveryNumber", DeliveryReportObject.DeliveryNumber);
using (SqlDataReader reader = command.ExecuteReader())
{
while (reader.Read())
{
CSerieses.Add(new CSerieses(reader.GetString(0), reader.GetInt32(1), reader.GetBoolean(2)));
}
}
}
}
catch (Exception ex)
{
LocalPulserDBManagerInstance.WriteLog(ex.StackTrace, ex.Message);
}
return CSerieses;
}
I try to combine them :
public static List<CSerieses> GetCSerieses(DeliveryReportObject DeliveryReportObject,
SqlConnection co = null,SqlTransaction tran = null)
{
List<CSerieses> CSerieses = new List<CSerieses>();
try
{
using (co ?? new SqlConnection(connectionString))
{
if (co.IsOpened() == false)
{
co.Open();
}
string query = "SELECT [CSeriesNum],[CCount],[Mark] from [Serieses] " +
"where [TestPrimary]=#deliveryNumber";
SqlCommand command = new SqlCommand(query, co, tran);
if(tran != null)
{
command.Transaction = tran;
}
command.Parameters.AddWithValue("#deliveryNumber", DeliveryReportObject.DeliveryNumber);
using (SqlDataReader reader = command.ExecuteReader())
{
while (reader.Read())
{
CSerieses.Add(new CSerieses(reader.GetString(0), reader.GetInt32(1), reader.GetBoolean(2)));
}
}
}
}
catch (Exception ex)
{
LocalPulserDBManagerInstance.WriteLog(ex.StackTrace, ex.Message);
}
return CSerieses;
}
It does not work for me. I have no idea how to check if it null in using and if yes to create a new instance of SqlConnection that should close at the end of the using statement
And I do it the right way anyway?
This is a major problem:
using (co ?? new SqlConnection(connectionString))
If co is passed in, then you don't own it - the caller does - so: you shouldn't be disposing it. What I would suggest here is:
bool ownConnection = false;
try
{
if (co is null)
{
ownConnection = true;
co = new SqlConnection(...);
co.Open();
}
// your code here
}
finally
{
if (ownConnection)
{
co?.Dispose();
}
}
or wrap that up in a helper - perhaps a custom disposable that takes a connection and connection string:
public readonly struct ConnectionWrapper : IDisposable
{
private readonly bool owned;
public SqlConnection Connection { get; }
public ConnectionWrapper(SqlConnection connection, string connectionString)
{
if (connection is null)
{
owned = true;
Connection = new SqlConnection(connectionString);
Connection.Open();
}
else
{
owned = false;
Connection = connection;
}
}
public void Dispose()
{
if (owned)
{
Connection?.Dispose();
}
}
}
then you can just use:
using var wrapped = new ConnectionWrapper(co, connectionString);
// your code, using wrapped.Connection
This seems that kind of situation that perfectly fits the overload concept.
The GetCSerieses method should have two versions, the first one builds its own connection and transaction, the second one takes both a non optional connection and a non optional transaction. The first one, after creating the connection and the transaction calls the second one.
Now if a third method requires a call the GetCSerieses could pass its own connection and transaction, while a call without them will be handled by the first overload
public static List<CSerieses> GetCSerieses(DeliveryReportObject DeliveryReportObject)
{
using(SqlConnection con = new SqlConnection(......))
{
try
{
con.Open();
using(SqlTransaction tran = con.BeginTransaction())
{
return GetCSerieses(DeliveryReportObject, con, tran);
}
// Or, if you don't need a transaction you could call the
// overload passing null
// return GetCSerieses(DeliveryReportObject, con, null);
}
catch(Exception ex)
{
LocalPulserDBManagerInstance.WriteLog(ex.StackTrace, ex.Message);
return null; // ?? or return new List<CSerieses>();
}
}
}
public static List<CSerieses> GetCSerieses(DeliveryReportObject DeliveryReportObject, SqlConnection co, SqlTransaction tran)
{
List<CSerieses> CSerieses = new List<CSerieses>();
try
{
// We don't own the connection and the transaction object.
// Whoever passed them to us is responsible of their disposal.
string query = "......";
SqlCommand command = new SqlCommand(query, co, tran);
command.Transaction = tran;
....
}
catch (Exception ex)
{
LocalPulserDBManagerInstance.WriteLog(ex.StackTrace, ex.Message);
}
return CSerieses;
}

return type array

Hello guys im newbie in C# Programming, i have problem when return array type in C# function. The return value its from query database(the code below):
//determine list of lesson
private string fillTreeLesson(string course_id)
{
string connectionString = "server=localhost; database=moodle; user=root; password=";
using (MySqlConnection con = new MySqlConnection(connectionString))
{
try
{
con.Open();
MySqlCommand cmnd = con.CreateCommand();
string sql = "select name from lesson where course like '%"+course_id+"%'";
cmnd.CommandText = sql;
MySqlDataReader reader = cmnd.ExecuteReader();
while (reader.Read())
{
lesson_name = reader.GetString("name");
}
con.Close();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
return lesson_name;
}
So my questions are:
I want to return type become array, can you give me advice?
How i can use above function, and call by index of return value. if i will call like these(fillTreeLesson is the name of function i will use):
treeOutline.Nodes[counter_node].Nodes.Add(fillTreeLesson(course_name));
thanks for attention
You can make it easily using a list as:
public List<string> FillTreeLesson(string course_id)
{
List<string> returnList = new List<string>();
try
{
con.Open();
MySqlCommand cmnd = con.CreateCommand();
string sql = "select name from lesson where course like '%"+course_id+"%'";
cmnd.CommandText = sql;
MySqlDataReader reader = cmnd.ExecuteReader();
while (reader.Read())
{
lesson_name = reader.GetString("name");
returnList.Add(lesson_name);
}
con.Close();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
return returnList;
}
In order to call this function,
Firstly make the List to contain your result:
List<string> namesList = new List<string>();
Then call the function and take its output in namesList we just
created:
namesList = FillTreeLesson("CS-502");
After it, you can easily get those strings one by one using
foreach loop:
foreach(string currentName in namesList)
{
//Whatever you want to do with currentName
}
OR
You can use for loop (Preferable if you want to use them in some
order using their index value or have to change their values later
on)
for(int i=0; i<namesList.Count; i++)
{
//Your code. You should access given element as namesList[i]
}

Generic Method to return multiple resultsets ado.net

I have the below generic method that works very well in reading 1 resultset.I have been fiddling trying to write a generic method to return multiple resulset
Could you give me a hand or suggestion how I could turn the below one into a method that returns/handle multiple resultset.
usage:
const string spName = "GetCountry";
object[] parms = { "#CountryId", countryId };
myDto dto = spName.GetOne(ToDto, parms);
return model;
public static T GetOne<T>(this string spName,
Func<IDataReader, T> createObject,
object[] parms = null)
{
try
{
using (var connection = new SqlConnection(ConnectionString))
{
using (var command = new SqlCommand())
{
command.Connection = connection;
command.CommandType = CommandType.StoredProcedure;
command.CommandText = spName;
command.SetParameters(parms);
connection.Open();
T t = default(T);
var reader = command.ExecuteReader();
if (reader.Read())
{
t = createObject(reader);
}
return t;
}
}
}
catch (SqlException ex)
{
etc......
}
return default(T);
}
an example would be :A customer with Addresses (SP will return a customer all the related addresses by returning 2 resultsets inside a sp.)
Many thanks for any suggestions

How to check mysql db for an entry with less code

Hi i want to know if there is a more simplified method than the one im using now to find if there is an entry or not in a mysql db.
public static bool check_db_entry(string query)
{
using (var conn = new MySqlConnection(DbMethods.constr))
{
using (var cmd = new MySqlCommand(query, conn))
{
conn.Open();
using (var rdr = cmd.ExecuteReader())
{
if (rdr.Read() == false)
{
return false;
}
else
{
return true;
}
}
}
}
}
Leaving aside for now that fact that methods that accept only sql strings are inherently unsafe, this smells wrong to me. If you're going to have public methods that accept arbitrary sql commands (remember: I said "If"), then undoubtedly you have one that returns the data directly. You should rely on that as your base. Here's an example:
private static IEnumerable<IDataRecord> GetDataImpl(string query)
{
using (var conn = new MySqlConnection(DbMethods.constr))
using (var cmd = new MySqlCommand(query, conn))
{
conn.Open();
using (var rdr = cmd.ExecuteReader())
{
yield return rdr;
}
}
}
public static bool check_db_entry(string query)
{
return GetDataImpl(query).Any();
}
Note there is a reason I listed the first method as private. As written, it can have weird side effects if you don't first copy each element in the reader before returning it to a higher abstraction level in your program. But you can get the public version easily enough:
public static IEnumerable<T> GetData<T>(string query, Func<IDataRecord,T> copy)
{
return GetDataImpl(query).Select(copy);
}
Taking aside your design issues pointed in the question's comments, if you want to check the existence an entry in the database, you should always query with COUNT(*): SELECT COUNT(*) FROM yourTable [WHERE theCondition].
If that is all you pass to your function, you can then simply with:
public static bool check_db_entry(string query)
{
using (var conn = new MySqlConnection(DbMethods.constr))
{
conn.Open();
using (var cmd = new MySqlCommand(query, conn))
{
return (int)cmd.ExecuteScalar() == 1;
}
}
}
And if you want to streamline it:
public static bool check_db_entry(string query)
{
using (var conn = new MySqlConnection(DbMethods.constr))
using (var cmd = conn.CreateCommand())
{
conn.Open();
cmd.CommandText = query;
return (int)cmd.ExecuteScalar() == 1;
}
}

Categories

Resources