c# SQL to List error - c#

real newbie question and I would appreciate any support you could offer.
I am trying to output SQL results to a list. But when I return the list I get
System.Collections.Generic.List`1[System.String]
This my code, any ideas? Thank you in advance.
public static List<string> GetDTSXPackages()
{
List<String> packages = new List<String>();
using (SqlConnection connection = new SqlConnection(SQLConnectionString()))
{
string query = "SELECT PackageName FROM SharedServices.DTSXPackages";
using (SqlCommand command = new SqlCommand(query, connection))
{
connection.Open();
using (SqlDataReader reader = command.ExecuteReader())
{
while (reader.Read())
{
packages.Add(reader["PackageName"].ToString());
}
}
connection.Close();
}
}
return packages;
}
static void Main(string[] args)
{
List<string> x = SQLAccessLayer.GetDTSXPackages();
foreach (var package in x)
{
Console.WriteLine(x);
}
}

It is not an error
By doing a loop you can see all values
foreach(var i in Packages)
{
Console.WriteLine(i);
}

Change
Console.WriteLine(x);
To
Console.WriteLine(package);

Related

SqlDataReader only reads partial data from JSON column

I have the following code but it only reads the last part of the JSON value:
public string GetUsersJson(long systemOrgId)
{
var query = #"DECLARE #OrgId bigint = #systemOrgId
SELECT e.OrgId, e.Id, e.FirstName, e.LastName
FROM [Internal].[Employee] e
WHERE OrgId = #OrgId and IsActive=1
FOR JSON PATH, ROOT('Users');";
var json = ExecuteSqlCommandWithJsonResponse(query, systemOrgId);
return json;
}
private string ExecuteSqlCommandWithJsonResponse(string queryString, long systemOrgId)
{
var result = "";
using (SqlConnection connection = new SqlConnection(_systemConnectionString))
{
using (var cmd = connection.CreateCommand())
{
connection.Open();
cmd.CommandText =queryString;
cmd.Parameters.AddWithValue("#systemOrgId", systemOrgId);
using (var reader = cmd.ExecuteReader())
{
while (reader.Read())
{
result = reader.GetString(reader.GetOrdinal("JSON_F52E2B61-18A1-11d1-B105-00805F49916B"));
}
}
}
}
return result;
}
If I use if instead I get the first part.
if (reader.Read())
{
result = reader.GetString(reader.GetOrdinal("JSON_F52E2B61-18A1-11d1-B105-00805F49916B"));
}
According to the SqlDataReader Class documentation a while (reader.Read()) should be used.
https://learn.microsoft.com/en-us/dotnet/api/system.data.sqlclient.sqldatareader?view=dotnet-plat-ext-7.0#examples
Adapting the code to look more like the MS example also gives the same result:
private string ExecuteSqlCommandWithJsonResponse(string queryString, long systemOrgId)
{
var result = "";
using (SqlConnection connection = new SqlConnection(_systemConnectionString))
{
var cmd = connection.CreateCommand();
connection.Open();
cmd.CommandText = queryString;
cmd.Parameters.AddWithValue("#systemOrgId", systemOrgId);
var reader = cmd.ExecuteReader();
while (reader.Read())
{
result = reader.GetString(reader.GetOrdinal("JSON_F52E2B61-18A1-11d1-B105-00805F49916B"));
}
reader.Close();
}
return result;
}
Using a standard concatenate strings with += easily solved it. I hope this can help someone else since this information is missing in the examples I have seen.
https://learn.microsoft.com/en-us/dotnet/csharp/how-to/concatenate-multiple-strings#-and--operators
private string ExecuteSqlCommandWithJsonResponse(string queryString, long systemOrgId)
{
var result = "";
using (SqlConnection connection = new SqlConnection(_systemConnectionString))
{
using (var cmd = connection.CreateCommand())
{
connection.Open();
cmd.CommandText =queryString;
cmd.Parameters.AddWithValue("#systemOrgId", systemOrgId);
using (var reader = cmd.ExecuteReader())
{
while (reader.Read())
{
result += reader.GetString(reader.GetOrdinal("JSON_F52E2B61-18A1-11d1-B105-00805F49916B"));
}
}
}
}
return result;
}

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.

How to Get Database Names With all Tables and procedures,views etc in c# Tree view

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.

Returning Data Rows to List<string> with SqlDataReader

I'm trying to create a generic SqlDataReader which converts a table with 33 columns into a list. I would like each list item to contain all 33 column values for each row.
However, my code is assigning each value to an individual list item.
So instead of 1000 list items = 1000 rows of data, I have 33,000 list items.
I would prefer to use a list over a datatable, because the list comparisons I need to do are much simpler.
How can I have 1000 list items with 33 values each?
public static List<string> loadSQL(String query, String connectString)
{
List<string> dataList = new List<string>();
using (SqlConnection connection = new SqlConnection(connectString))
{
using (SqlCommand command = new SqlCommand(query, connection))
{
connection.Open();
using (SqlDataReader reader = command.ExecuteReader())
{
while (reader.Read())
{
for (int i = 0; i < reader.FieldCount; i ++)
{
dataList.Add(Convert.ToString(reader.GetValue(i)));
}
}
}
}
return dataList;
}
}
... update ...
corrected to the following. It returns the list items correctly. However, my list contains 33,000 items containing 33 items each. How can I control the loop so it stops after 1000 rows?
using (SqlConnection connection = new SqlConnection(connectString))
{
using (SqlCommand command = new SqlCommand(query, connection))
{
connection.Open();
using (SqlDataReader reader = command.ExecuteReader())
{
while (reader.Read())
{
List<string> tempRow = new List<string>();
for (int i = 0; i < reader.FieldCount; i ++)
{
tempRow.Add(Convert.ToString(reader.GetValue(i)));
}
dataList.Add(tempRow);
}
}
}
}
The best option for you to do this task is DataTable, But you don't want to use it. So, the net option will be, Create a class based on the query-output then use a List<objectOftheClass>. But in your case, the Input query will be changed all times so a common class will not be meaningful Since you are trying to make it generic. So the option you can follow is List<List<string>> or List<List<object>>. As per this the method signature will be like the following:
public static List<object[]> loadSQL(string query, string connectString)
{
List<object[]> dataList = new List<object[]>();
using (SqlConnection connection = new SqlConnection(connectString))
{
using (SqlCommand command = new SqlCommand(query, connection))
{
connection.Open();
using (SqlDataReader reader = command.ExecuteReader())
{
while (reader.Read())
{
object[] tempRow = new object[reader.FieldCount];
for (int i = 0; i < reader.FieldCount; i++)
{
tempRow[i] = reader[i];
}
dataList.Add(tempRow);
}
}
}
}
return dataList;
}
Why List<object>? why not `List?:
The reader will give you the column data as the same type of column in the table. If it is object then you need not convert it every time.
** Note:-** Change String to string for the arguments in the method signature. You can find a reason here
You can use a List<List<string>> like this:
public static List<List<string>> loadSQL(String query, String connectString)
{
List<List<string>> dataList = new List<List<string>>();
using (SqlConnection connection = new SqlConnection(connectString))
{
using (SqlCommand command = new SqlCommand(query, connection))
{
connection.Open();
using (SqlDataReader reader = command.ExecuteReader())
{
while (reader.Read())
{
var l = new List<string>();
for (int i = 0; i < reader.FieldCount; i ++)
{
l.Add(Convert.ToString(reader.GetValue(i)));
}
dataList.Add(l);
}
}
}
return dataList;
}
}
You can use an array within the list to achieve what you are trying to do. Here is a quick example using your code:
public static List<string[]> loadSQL(String query, String connectString)
{
List<string[]> dataList = new List<string[]>();
using (SqlConnection connection = new SqlConnection(connectString))
{
using (SqlCommand command = new SqlCommand(query, connection))
{
connection.Open();
using (SqlDataReader reader = command.ExecuteReader())
{
int rowcounter = 0;
while (reader.Read())
{
string[] value = new string[reader.FieldCount];
for (int i = 0; i < reader.FieldCount; i ++)
{
value[i] = Convert.ToString(reader.GetValue(i));
}
dataList.Add(value);
rowcounter++;
}
}
}
return dataList;
}
Alertnately, if you want to use the List, you will need to embed the values a single string, using a comma separator or something similar.

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