SQLite real all string rows - c#

I'm trying to get all string type rows with specified id from a table.
I keep getting the error Object reference not set to an instance of an object..
I tried try {} catch {} to see if there was a more detailed exception, which there wasn't.
This is what I've got.
var ReadReasons = _db.CreateCommand();
ReadReasons.CommandText = $"SELECT reason FROM Warns WHERE id = '{user}' AND guildId = '{guild}'";
SqliteDataReader ReadReasonData = await ReadReasons.ExecuteReaderAsync();
if (ReadReasonData.HasRows)
{
while (ReadReasonData.Read())
{
foreach (var reas in ReadReasonData)
{
ReadWarnReason.Add((string)reas);
}
}
}

Related

Testing DB content, Zooming in on 1 value

I have a SQLite database that returns 1 number to
Select value from Income where symbol = "AE" and statementitem = "Revenues" and periodtype = "Annual" and yearmonth = "Dec 2019"; --1811.2
I use a bit of c# code to test this to make sure nothing is missed:
public string GetIncome(string dbFile, string symbol, string aq, string yearmonth)
{
var answer = String.Empty;
try
{
using (var con = new SQLiteConnection($"URI=file:{dbFile}"))
{
con.Open();
using var cmd = new SQLiteCommand(con)
{
CommandText = $"Select value from Income where symbol = '{symbol}' and statementitem = 'Revenues' and periodtype = '{aq}' and yearmonth = '{yearmonth}';"
};
using SQLiteDataReader dataReader = cmd.ExecuteReader();
while (dataReader.Read())
{
answer = dataReader.GetString(1);
}
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
throw ex;
}
return answer;
}
This errors out with System.IndexOutOfRangeException : Index was outside the bounds of the array.
Whats the best way to pick up on that value please?
See official docs:
https://learn.microsoft.com/en-us/dotnet/api/microsoft.data.sqlite.sqlitedatareader.getstring?view=msdata-sqlite-3.1.0#Microsoft_Data_Sqlite_SqliteDataReader_GetString_System_Int32_
It tells that GetString only argument is "The zero-based column ordinal". In your case you're trying to access second item (by index 1), but your query has only single field returned. Use index 0 in
answer = dataReader.GetString(0);

How to return names of all columns?

Searching, I found the PRAGMA as a possible solution for my problem, but it only returns the index of each column. There's any other method to return all columns names?
I thought using a For to go through my column indexes returning their names would works fine, but I dont exactly know how the syntax of this would be, either the stop condition.
void FillColumnList()
{
try
{
string check = "SELECT * FROM PRAGMA table_info(Produtos)";
sqlCon.Open();
SQLiteCommand tst2 = new SQLiteCommand(check, sqlCon);
SQLiteDataReader rdr2 = tst2.ExecuteReader();
if (rdr2.HasRows)
{
while (rdr2.Read())
{
string columns = rdr2[0].ToString();
Columns.Add(columns);
}
sqlCon.Close();
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message.ToString());
}
}
This code should return and fill the Global variable list Columns with the name of each column of "Produtos" table. Instead of it, my DataReader 'rdr2' return false in the HasRows, even when there's columns and Datas in my table "Produtos"
You can use the connection's GetSchema method to retrieve the column information. I'm using the following code to insert information my own class TableColumn not shown here:
string[] restrictions = new string[] { null, null, tableName };
using (DataTable columns = conn.GetSchema("Columns", restrictions)) {
int nameIndex = columns.Columns.IndexOf("COLUMN_NAME");
int ordinalPosIndex = columns.Columns.IndexOf("ORDINAL_POSITION");
int isNullableIndex = columns.Columns.IndexOf("IS_NULLABLE");
int maxLengthIndex = columns.Columns.IndexOf("CHARACTER_MAXIMUM_LENGTH");
int dataTypeIndex = columns.Columns.IndexOf("DATA_TYPE");
int isPrimaryKeyIndex = columns.Columns.IndexOf("PRIMARY_KEY");
int hasDefaultIndex = columns.Columns.IndexOf("COLUMN_HASDEFAULT");
int defaultValueIndex = columns.Columns.IndexOf("COLUMN_DEFAULT");
foreach (DataRow row in columns.Rows) {
var col = new TableColumn {
ColumnName = (string)row[nameIndex]
};
try {
col.ColumnNameForMapping = prepareColumnNameForMapping(col.ColumnName);
} catch (Exception ex) {
throw new UnimatrixExecutionException("Error in delegate 'prepareColumnNameForMapping'", ex);
}
col.ColumnOrdinalPosition = (int)row[ordinalPosIndex];
col.ColumnAllowsDBNull = (bool)row[isNullableIndex];
col.ColumnMaxLength = (int)row[maxLengthIndex];
string explicitDataType = ((string)row[dataTypeIndex]).ToLowerInvariant();
col.ColumnDbType = GetColumnDbType(explicitDataType);
col.ColumnIsPrimaryKey = (bool)row[isPrimaryKeyIndex];
col.ColumnIsIdentity = explicitDataType == "integer" && col.ColumnIsPrimaryKey;
col.ColumnIsReadOnly = col.ColumnIsIdentity;
if ((bool)row[hasDefaultIndex]) {
col.ColumnDefaultValue = GetDefaultValue(col.ColumnDbType, (string)row[defaultValueIndex]);
if (col.ColumnDefaultValue == null) { // Default value could not be determined. Probably expression.
col.AutoAction = ColumnAction.RetrieveAfterInsert;
}
}
tableSchema.ColumnSchema.Add(col);
}
}
You can simplify this code considerably if you only need the column names.

SQL query get error "Specified cast is not valid."

I have sample codes as below:-
public List<Announcement_User> announcementUser([FromBody]MyAnnouncementUser value)
{
MySqlConnection conn = WebApiConfig.conn();
MySqlCommand query = conn.CreateCommand();
query.CommandText = "select a.title,a.description,a.date_created,ua.read,ua.announcement_id,ua.user_announcement_id from announcement a left join user_announcement ua on a.announcement_id = ua.announcement_id where ua.user_id = #user_id";
query.Parameters.AddWithValue("#user_id", value.user_id);
var prodWishlist = new List<Announcement_User>();
try
{
conn.Open();
}
catch (MySql.Data.MySqlClient.MySqlException ex)
{
prodWishlist.Add(new Announcement_User(null, null,null, false, 0, 0, ex.ToString()));
}
MySqlDataReader fetch_query = query.ExecuteReader();
while (fetch_query.Read())
{
prodWishlist.Add(new Announcement_User(fetch_query["title"].ToString(), fetch_query["description"].ToString(), fetch_query["date_created"].ToString(), (bool)fetch_query["read"], fetch_query.GetInt32(4), fetch_query.GetInt32(5), null));
}
conn.Close();
return prodWishlist;
}
And I hit error as below:-
"Message": "An error has occurred.",
"ExceptionMessage": "Specified cast is not valid.",
"ExceptionType": "System.InvalidCastException",
Now I suspect the error caused by bool. May I know how can I write correct way for bool in(fetch_query.Read())? Please help. Thank you.
Try using the GetBoolean method:
fetch_query.GetBoolean("read")
I would recommend to you to retrieve all of the values beforehand using column name instead of column index and create new object when required parameters have values:
string title = fetch_query["title"].ToString();
string description = fetch_query["description"].ToString();
// ...
object read = fetch_query["read"];
object integer1 = fetch_query[4];
// ...
// newest C# approach
if ( read != null && bool.TryParse(read.ToString(), out bool b_read) )
// old c# approach
bool b_read = false;
if ( read != null && bool.TryParse(read.ToString(), out b_read) )
Check for every required property
prodWishlist.Add(
new Announcement_User(
title,
description,
// ..
b_read,
i_integer1
//...
));

Simple SQL query with DataContext

I have a web-site connected to a SQL Server database, and I want to add a simple SQL query to it (for administrators). I was hoping to use the DataContext, and run a query, then return the results as a simple list. Is there any way to do this?
Using
string full_query = "SELECT " + query;
IEnumerable<string> results = DB.DB().ExecuteQuery<string>(full_query);
Doesn't work, throwing errors where ints come through. Changing the template parameter to "object" doesn't help much either.
So I need to run a select statement, and return the results as a list on a page.
Any ideas?
Normally you would want to use:
var results = DB.DB().SqlQuery(full_query);
If you want insert/update/delete, you can use:
DB.DB().ExecuteSqlCommand(full_query);
Hope it helps.
After a bit of messing around, I found something that works. I am using a class called DatabaseResults to hold the results:
public class DatabaseResults
{
public List<string> ColumnNames { get; set; }
public List<List<string>> Rows { get; set; }
public DatabaseResults()
{
ColumnNames = new List<string>();
Rows = new List<List<string>>();
}
}
The method then goes and runs the query, grabbing the headings and putting them in the results objects. It then reads the rows, taking the strings of the column values. "query" is the string passed in. It is the "select" query, with the select bit missing.
DatabaseResults results = new DatabaseResults();
string full_query = "SELECT " + query;
DbConnection connection = DB.DB().Connection;
connection.Open();
var command = connection.CreateCommand();
command.CommandText = full_query;
try
{
using (var reader = command.ExecuteReader())
{
for (int i = 0; i < reader.FieldCount; i++)
{
results.ColumnNames.Add(reader.GetName(i));
}
while (reader.Read())
{
List<string> this_res = new List<string>();
for (int i = 0; i < reader.FieldCount; ++i)
{
this_res.Add(reader[i].ToString());
}
results.Rows.Add(this_res);
}
}
}
catch (Exception ex)
{
results.ColumnNames.Add("Error");
List<string> this_error = new List<string>();
this_error.Add(ex.Message);
results.Rows.Add(this_error);
}
finally
{
connection.Close();
}
I can't destroy the connection, as it is used by the systems db object, so I need to open and close it. The try/catch/finally makes sure this happens.

Is there a way to store data from a DataReader directly into a List<class> [duplicate]

This question already has answers here:
Generic list by using reflection
(4 answers)
Closed 7 years ago.
I'm creating a datareader from a SqlCommand and I currently store this information in a class with the following
private object PopulateObjectWithFields(SqlDataReader read, Type className)
{
var gd = Activator.CreateInstance(className);
for (int i = 0; i < read.FieldCount; i++)
{
var type = gd.GetType();
var fi = type.GetField(read.GetName(i));
if (fi != null)
{
if (!Convert.IsDBNull(read[i]))
{
try
{
fi.SetValue(gd, read[i]);
}
catch
{
throw new Exception(string.Format("Unable to set {0}. Class type {1}. DB Type {2}", read.GetName(i), fi.FieldType.Name, read[i].GetType().Name));
}
}
}
else
{
var pi = type.GetProperty(read.GetName(i));
if (pi != null)
{
if (!Convert.IsDBNull(read[i]))
{
try
{
pi.SetValue(gd, read[i]);
}
catch
{
throw new Exception(string.Format("Unable to set {0}. Class type {1}. DB Type {2}", read.GetName(i), fi.FieldType.Name, read[i].GetType().Name));
}
}
}
}
}
return gd;
}
This works perfectly. What I would like now is to put it in a List<class>, but I can't seem to get it right to create a List<class> dynamically.
Can someone maybe help me out with the syntax?
var MyList = List<MyClass> //This should be dynamically created
using(var reader = cmd.ExecuteReader())
{
while (reader.Read())
{
MyList.Add(PopulateObjectWithFields(read, MyClass));
}
}
LET ME CLARIFY
I don't know what columns is going to be in the reader, and I don't know what class is going to be passed in. I have a generic method that needs to populate ANY reader to ANY class as long as the column names match the property of fields in the class
You should use generics:
private T PopulateObjectWithFields<T>(SqlDataReader read) where T : new()
The implementation of your method stays the same, except that you use T instead of MyClass, and that you can instantiate the type directly using the constructor:
var gd = new T();
From there, you can easily store your objects in your list:
var MyList = List<MyClass> //This should be dynamically created
using(var reader = cmd.ExecuteReader())
{
while (reader.Read())
{
MyList.Add(PopulateObjectWithFields<MyClass>(read);
}
}

Categories