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;
}
}
Related
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.
Actually I want to fetch Database Names just like in SQL Management Studio have on the left Spinner Bar same as in C# tree View.
I am working on a system in which database control through Application,
so I fetched all database Names and All Tables Names but I not getting each Database with their tables, just like in SQL Management Studio, in which A database have 4 to 5 nodes includes(tables,views,procedures,etc). and also I fetched in a List so I want to fetch in a tree View just Like in SQL Management Studio.
for Backend(Business Logic) I used this and fetched All Database Names and all Tables without respective sequences.
public List<string> GetDatabaseList()
{
List<string> list = new List<string>();
using (SqlConnection con = new SqlConnection(MyConString))
{
con.Open();
using (SqlCommand cmd = new SqlCommand("SELECT name from
sys.databases", con))
{
using (SqlDataReader dr = cmd.ExecuteReader())
{
while (dr.Read())
{
list.Add(dr[0].ToString());
}
}
}
}
return list;
}
public List<string> GetTableList()
{
List<string> list = new List<string>();
using (SqlConnection con = new SqlConnection(MyConString))
{
con.Open();
using (SqlCommand cmd = new SqlCommand("SELECT name from
sys.Tables", con))
{
using (SqlDataReader dr = cmd.ExecuteReader())
{
while (dr.Read())
{
list.Add(dr[0].ToString());
}
}
}
}
return list;
}
and use it by front-end through anycomponents.datasource.
My expected output is that I want to get all respective nodes of database Just like in SQL Management Studio not only database and tables.
Well if you use sys.objects it'll list all database related objects. The only trick you need is to have a sysadmin account that can view all databases. Then, you can just target each database objects.
So, what you'll need is :
Get databases names list
Target each database objects
Filter objects as needed.
Here is a working example.
public enum DbObjectType
{
SYSTEM_TABLE,
USER_TABLE,
VIEW,
SQL_STORED_PROCEDURE
}
public class SqlServerDbObjects
{
const string connectionString = #"Data Source=.\;Initial Catalog=master;Integrated Security=True";
public IDictionary<string, IList<string>> GetDbObjects()
{
var dbNameList = GetDatabaseList();
var dbObj = new Dictionary<string, IList<string>>();
for (int x = 0; x < dbNameList.Count; x++)
{
var name = dbNameList[x];
var objList = GetDatabaseObjectsList(name, DbObjectType.USER_TABLE);
dbObj.Add(name, objList);
}
return dbObj;
}
public IList<string> GetDatabaseList()
{
const string sql = "SELECT [name] FROM sys.databases WHERE database_id > 4";
return Reader(sql, "name");
}
public IList<string> GetDatabaseObjectsList(string databaseName, DbObjectType objType)
{
StringBuilder sb = new StringBuilder(string.Empty);
if (string.IsNullOrEmpty(databaseName))
throw new NullReferenceException("You must specify a database");
else
sb.Append($"SELECT [name] FROM [{databaseName}].sys.objects WHERE [type_desc] = '{objType.ToString()}'");
return Reader(sb.ToString(), "name");
}
public IList<string> Reader(string query, string columnName)
{
var list = new List<string>();
try
{
using (SqlConnection connection = new SqlConnection(connectionString))
using (SqlCommand command = new SqlCommand(query, connection))
{
connection.Open();
using (SqlDataReader dataReader = command.ExecuteReader())
{
while (dataReader.Read())
{
list.Add(dataReader[columnName].ToString());
}
}
}
return list;
}
catch (SqlException ex)
{
// do stuff for handling sql errors.
return null;
}
}
}
Then you just do :
var test = new SqlServerDbObjects().GetDbObjects();
I have tried to make it as simple as I could, so you can get the idea.
I've recently explored c# to myself. But stuck with this problem.
So I have a method dbExec
public void dbExec(Action<OleDbCommand> func)
{
using (var conn = new OleDbConnection(connStr))
{
conn.Open();
var cmd = conn.CreateCommand();
func(cmd);
}
}
delegate,
public delegate void DelCmd(OleDbCommand cmd);
and another method:
public ICollection<string> CheckUserPermissions()
{
List<string> logins = new List<string>();
DelCmd delCmd = delegate(OleDbCommand cmd)
{
cmd.CommandText = "SELECT PERMISSIONS.LOGIN FROM PERMISSIONS";
using (var rdr = cmd.ExecuteReader()) while (rdr.Read()) logins.Add(rdr["LOGIN"].ToString());
};
dbExec(delcmd);
return logins;
}
The problem with dbExec(delcmd); statement. The error is "delcmd doesn't exist in current context". How to pass an anonymous method as a parameter to another method with Action declared parameter?
You could also avoid defining a delegate altogether.
Like this:
public ICollection<string> CheckUserPermissions()
{
List<string> logins = new List<string>();
Action<OleDbCommand> delCmd = cmd =>
{
cmd.CommandText = "SELECT PERMISSIONS.LOGIN FROM PERMISSIONS";
using (var rdr = cmd.ExecuteReader())
while (rdr.Read()) logins.Add(rdr["LOGIN"].ToString());
};
dbExec(delCmd);
return logins;
}
Edit: I actually mean what Servy wrote in the comment on the other answer, but he described it way better.
You have a typo - it should be delCmd instead of delcmd. C# is a case-sensitive language
UPDATE: DelCmd is not same as Action<OleDbCommand> - that is different types, and you even can't cast delegates to each other. But you can create new action delegate:
dbExec(new Action<OleDbCommand>(delCmd));
If I have code like so:
public T ExecuteQuery<T>(Func<IDataReader, T> getResult, string query, params IDataParameter[] parameters)
{
using (SqlConnection conn = new SqlConnection(this.DefaultConnectionString))
{
conn.Open();
// Declare the parameter in the query string
using (SqlCommand command = new SqlCommand(query, conn))
{
foreach (var parameter in parameters)
{
command.Parameters.Add(parameter);
}
command.Prepare();
using (SqlDataReader dr = command.ExecuteReader())
{
return getResult(dr);
}
}
}
}
public string GetMySpecId(string dataId)
{
return ExecuteQuery(
dr =>
{
if (dr.Read())
{
return dr[0].ToString();
}
return string.Empty;
},
#"select ""specId"" from ""MyTable"" where ""dataId"" = :dataId",
new SqlParameter("dataId", dataId));
}
}
How do I ensure that the
new SqlParameter("dataId", dataId));
piece of code is passing in a text or maybe an integer? Also how does the #"select..." actually work as I'm familiar to:
select id from mytable where dataId = #dataID;
I'm not sure that's parameterized properly. Take a look at the below modified code that will ensure the parameters are added properly and simplify the construction of the call to ExecuteQuery (in my opinion of course). This is pretty straight forward. The select statement is parameterized properly because it's using the #varname syntax:
"select \"specId\" from \"MyTable\" where \"dataId\" = #dataId"
Further, the parameters are typed properly because of the AddWithValue method:
command.Parameters.AddWithValue(parameter.Key, parameter.Value);
Finally, using the dictionary to send in the parameters keeps it pretty simple to construct the parameters from any structure, whether that be parameter values, or even an object.
public T ExecuteQuery<T>(Func<IDataReader, T> getResult, string query, Dictionary<string, object> parameters)
{
using (SqlConnection conn = new SqlConnection(this.DefaultConnectionString))
{
conn.Open();
// Declare the parameter in the query string
using (SqlCommand command = new SqlCommand(query, conn))
{
foreach (var parameter in parameters)
{
command.Parameters.AddWithValue(parameter.Key, parameter.Value);
}
command.Prepare();
using (SqlDataReader dr = command.ExecuteReader())
{
return getResult(dr);
}
}
}
}
public string GetMySpecId(string dataId)
{
return ExecuteQuery(
dr =>
{
if (dr.Read())
{
return dr[0].ToString();
}
return string.Empty;
},
"select \"specId\" from \"MyTable\" where \"dataId\" = #dataId",
new Dictionary<string, object>() { { "#dataId", dataId } });
}
P.S. - the # before the string in your example is just an escape sequence used in C#.
I have encountered a problem with getting locked out of SQLite file.
I am currently working on employee register for my company and use SQLite as a database of choice. From my program, I populated the database using scripts I prepared. Reading goes well and all, but once I make any change to the employee at all, the program freezes at "database is locked" exception.
I created a static class called SQLiteManager where I have all methods involving the DB. I then call them from Windows Forms like
foreach (Employee em in SQLiteManager.getEmployees()){
//filling the fields }
calls
public static List<Employee> getEmployees() {
List<Employee> ret = new List<Employee>();
SQLiteDataReader reader;
using (SQLiteConnection dbc = new SQLiteConnection(databaseConnectionString))
using (SQLiteCommand cmd = dbc.CreateCommand())
{
openConnection(dbc);
cmd.CommandText = "select ID_Employee from Employee";
reader = cmd.ExecuteReader();
while (reader.Read()) { ret.Add(extractEmployee(reader["ID_Employee"].ToString(), false)); }
}
return ret;
}
I always use this "using" notations in ALL other methods that call ExecuteReader() in them.
Method that I use to crunch the script and populate the DB:
public static bool processSqlCommandFile(string file)
{
try
{
using (StreamReader sr = new StreamReader(file))
{
string line;
while ((line = sr.ReadLine()) != null)
{
using (SQLiteConnection dbc = new SQLiteConnection(databaseConnectionString))
using (SQLiteCommand cmd = dbc.CreateCommand())
{
openConnection(dbc);
cmd.CommandText = line;
cmd.ExecuteNonQuery();
}
}
sr.Close();
}
}
catch (Exception ex)
{
return false;
}
return true;
}
and finally, the sore method
public static bool alterEmployee(Employee e) {
using (SQLiteConnection dbc = new SQLiteConnection(databaseConnectionString))
using (SQLiteCommand cmd = dbc.CreateCommand())
{
openConnection(dbc);
cmd.CommandText = "Update Employee SET name = '"+e.name+"' where ID_Employee = "+e.id;
//Alter the command once this shiet works
cmd.ExecuteNonQuery();
}
return true;
}
Yet it always gets frozen. By using the using(SQLiteConnection...) I thought I am getting rid of the "sleepy" connections that would somehow lock it, but the trouble is somewhere else.
Can you help me identify the problem or tell me steps to identify it myself?
Thank you