Populating list from Database - System.InvalidCastException - c#

I have a simple class that I want to populate a list of from a database:
class Foreman
{
public int ForeBadge { get; set; }
public string ForeName { get; set; }
}
Heres my method that is supposed to return a list of Foreman objects:
static public List<Foreman> getForeman()
{
connectionString.DataSource = "server";
connectionString.InitialCatalog = "db";
connectionString.UserID = "tunnelld";
connectionString.Password = "pw";
string queryString = "SELECT * FROM [QTRAXAdmin].[vwQT_Foreman]";
List<Foreman> list;
// Creates a SQL connection
using (var connection = new SqlConnection(connectionString.ToString()))
{
using (var command = new SqlCommand(queryString, connection))
{
connection.Open();
using (var reader = command.ExecuteReader())
{
list = new List<Foreman>();
while (reader.Read())
list.Add(new Foreman { ForeBadge = reader.GetInt32(0), ForeName = reader.GetString(1) });
}
}
connection.Close();
return list;
}
The method is erroring out at list.Add(new Foreman { ForeBadge = reader.GetInt32(0), ForeName = reader.GetString(1) });
with: An unhandled exception of type 'System.InvalidCastException' occurred in System.Data.dll
To me it looks like I'm using the correct variable types. What am I doing wrong?

Try using the field names explicitly in your query to ensure columns are coming back in the order you think they are. That is the most suspicious thing I see. Alternatively you could use column names in your using block. And of course double check the column definition in the database.

Assuming the table column names are forebadge and forename, replace:
string queryString = "SELECT * FROM [QTRAXAdmin].[vwQT_Foreman]";
with:
string queryString = "SELECT FOREBADGE, FORENAME FROM [QTRAXAdmin].[vwQT_Foreman]";

Related

Display 2 SQL tables using c#

I am trying to display two SQL tables from the same DB in a webpage but the code below is displaying the 'BottomStock' table twice and everything I try seems to either get part of the data from the 'TopStock' table or none at all. I have scroll through countless forums but I have been unable to find a suitable solution. Any help would be appreciated.
public class Test4Model : PageModel
{
public List<FreezerInfo> listTopFreezer = new List<FreezerInfo>();
public List<FreezerInfo> listBottomFreezer = new List<FreezerInfo>();
public void OnGet()
{
try
{
using (var connection = new SqlConnection("Data Source=SDS-
LAPTOP\\SQLEXPRESS;Initial Catalog=test;user id=sa;password=wis09"))
{
connection.Open();
using (var command = connection.CreateCommand())
{
command.CommandText = "SELECT * FROM TopStock";
command.ExecuteNonQuery();
command.CommandText = "SELECT * FROM BottomStock";
command.ExecuteNonQuery();
using (SqlDataReader reader = command.ExecuteReader())
{
while (reader.Read())
//reader.Read();
{
FreezerInfo TopStock = new FreezerInfo();
TopStock.Description = reader.GetString(1);
TopStock.Quantity = reader.GetString(2);
listTopFreezer.Add(TopStock);
FreezerInfo BottomStock = new FreezerInfo();
BottomStock.Description = reader.GetString(1);
BottomStock.Quantity = reader.GetString(2);
listBottomFreezer.Add(BottomStock);
}
}
}
}
}
catch (Exception ex)
{
}
}
}
public class FreezerInfo
{
public string Description { get; set; }
public string Quantity { get; set; }
}
You are using SqlCommand completely wrong. ExecuteNonQuery does not return results. Only ExecuteScalar or ExecuteReader do. Furthermore, you have two batches each with a SELECT, but you are only executing one and somehow expecting the results to be interleaved.
I would advise you to use one batch of two SELECT statements, you can use NextResult to move to the next resultset within the batch.
Store your connection string in a settings file, not hard-coded.
Only select the columns you need, rather than SELECT *.
Use column names rather than ordinals, especially if you are using SELECT *.
Do not swallow exceptions. Handle them or allow them to bubble back to the caller.
Consider using async to allow the caller to continue asynchronously.
Reconsider the data types of the columns.
Consider why you have two almost identical tables in the first place. Perhaps they should be merged.
public void OnGet()
{
try
{
const string query = #"
SELECT Description, Quantity
FROM TopStock;
SELECT Description, Quantity
FROM BottomStock;
";
using var connection = new SqlConnection(connectionStringFromSettingsFileHere);
using var command = new SqlCommand(query, connection);
connection.Open();
using (SqlDataReader reader = command.ExecuteReader())
while (reader.Read())
{
FreezerInfo TopStock = new FreezerInfo
{
Description = (string)reader["Description"],
Quantity = (string)reader["Quantity"], // shouldn't it be an int???
};
listTopFreezer.Add(TopStock);
}
reader.NextResult();
while (reader.Read())
{
FreezerInfo BottomStock = new FreezerInfo
{
Description = (string)reader["Description"];
Quantity = (string)reader["Quantity"], // shouldn't it be an int???
};
listBottomFreezer.Add(BottomStock);
}
}
catch (Exception ex)
{
// exception handling here. DO NOT SWALLOW
}
}

C# Read multiple rows from different columns sharing same Foreign key SQL

I want an efficient way to retrieve all info that shares the same Foreign key in a table, and store the data in list/array.
I can read several rows from one column:
SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["ConnectionString"].ConnectionString);
int idForeignKey = inputIdFkey //Implemented on the WebPage for testing purposes
List<string> result = new List<string>();
string oString = "Select Column from Table where foreignKey = #fKey";
conn.Open();
SqlCommand oCmd = new SqlCommand(oString, conn);
oCmd.Parameters.AddWithValue("#fKey", idForeignKey);
using (SqlDataReader oReader = oCmd.ExecuteReader())
{
while (oReader.Read())
{
result.Add(oReader.GetString(0));
}
}
conn.Close();
And I can read several columns if I am targeting one specific row:
int sqlData1;
int sqlData2;
int sqlData3;
string oString = "Select * from Table where TableID = #tId";
SqlCommand oCmd = new SqlCommand(oString, conn);
oCmd.Parameters.AddWithValue("#tId", 1001);
conn.Open();
using (SqlDataReader oReader = oCmd.ExecuteReader())
{
while (oReader.Read())
{
sqlData1 = oReader["Row1"].ToString();
sqlData2 = oReader["Row2"].ToString();
sqlData3 = oReader["Row3"].ToString();
}
}
conn.Close();
But I would love to be able to read all/specific data that has the same foreign key. So I want to be able to retrieve several rows, save them to a list, and retrieve several other row data from a different column that shares the same foreign key.
I imagine it to be something like this:
int idForeignKey = inputIdFkey //Implemented on the WebPage for testing purposes
List<int> intList = new List<int>();
List<string> stringList = new List<string>();
List<DateTime> dateList = new List<DateTime>();
string oString = "Select * from Table where ForeignKey = #fKey";
conn.Open();
SqlCommand oCmdSleep = new SqlCommand(oString, conn);
oCmdSleep.Parameters.AddWithValue("#fKey", idForeignKey);
using (SqlDataReader oReader = oCmdSleep.ExecuteReader())
{
while (oReader.Read())
{
intList.Add(oReader["Column1"].GetDateTime(0));
dstringList.Add(oReader["Column3"].GetDateTime(0));
dateList.Add(oReader["Column4"].GetDateTime(0));
}
}
conn.Close();
But this does not working... Please advice me
If you use something like Dapper it would simplify mapping your query result to List<T>.
Add Dapper to your project using nuget.
Install-Package Dapper -Version 1.50.5
Add using for Dapper at the top of your class where you are running the query.
using Dapper;
Add a class that matches the structure of your query results. There are ways to do this using a script or utility app. Here's an app for that.
public class MyClass
{
public int MyId { get; set; }
public string MyName { get; set; }
public DateTime MyDateTime { get; set; }
}
Then where you run the query do so like this.
using (conn = new SqlConnection(ConfigurationManager.ConnectionStrings["ConnectionString"].ConnectionString))
{
var MyList = conn.Query<MyClass>(#"select * from Table where ForeignKey = #fKey",
new { fKey = "SomeKey" }).ToList();
}
Once the query runs, you can then iterate across MyList.
foreach (var myItem in MyList)
{
// Do something with myItem
}
If you want to bind the results, simply remove .ToList() from the end of the query as its default is IObservable<T>.
No need to give the name of the column, simply use the already established index:
int idForeignKey = inputIdFkey //Implemented on the WebPage for testing purposes
List<int> intList = new List<int>();
List<string> stringList = new List<string>();
List<DateTime> dateList = new List<DateTime>();
string oString = "Select * from Table where ForeignKey = #fKey";
conn.Open();
SqlCommand oCmdSleep = new SqlCommand(oString, conn);
oCmdSleep.Parameters.AddWithValue("#fKey", idForeignKey);
using (SqlDataReader oReader = oCmdSleep.ExecuteReader())
{
while (oReader.Read())
{
intList.Add(oReader.GetDateTime(0));
dstringList.Add(oReader.GetDateTime(3));
dateList.Add(oReader.GetDateTime(4));
}
}
conn.Close();
Here is a breakdown:
listVariable.Add(oReader.GetDataType("Index of column"));
This way you get to retrieve all row data for shared foreign key and the option to do so for as many columns as you want.

C# SQL stored procedure call values

Trying to communicate with the database, I am little bit confused about how to pass a value as a parameter(for ex. an itemID) and get back the records that are having this ID.
Here is my stored procedure:
ALTER PROCEDURE [dbo].[sp_lightItem]
(
#itemID INT
)
AS
BEGIN
SELECT [itemID],
[itemName],
[itemLocation],
[itemChBy]
FROM [dbo].[Item]
WHERE itemSystemType='E' and itemID=#itemID ORDER BY itemID DESC;
END
And this is my c# code so far..
public string LoadItemNew(int ItemID)
{
var acf = new AcFunctions();
var newstorevalue = SqlHelper.ExecuteDataset(acf.AcConn(), "sp_lightItem", ItemID);
}
As you can see in stored procedure, what I want is to get back those 4 elements:
[itemID],[itemName],[itemLocation],[itemChBy]
Unfortunately I do not know how to get them back/how to call them in c# function.
Any help is welcome.
i dont have enough repo to comment , can you provide the definition of
AcFunctions();
i am sure you it must be returning ConnectionString
try this
public string LoadItemNew(int ItemID)
{
var acf = new AcFunctions();
var newstorevalue = SqlHelper.ExecuteDataset(acf.AcConn(), "sp_lightItem", new SqlParameter ("#itemID",ItemID));
}
Edit 1
try this
public string LoadItemNew(int ItemID)
{
List<string> testList = new List<string>();
var acf = new AcFunctions();
var newstorevalue = SqlHelper.ExecuteReader(acf.AcConn(), "sp_lightItem", new SqlParameter ("#itemID",ItemID));
if(newstorevalue.HasRows)
{
while(newstorevalue.Read())
{
testList.Add(newstorevalue["itemID"].ToString());
testList.Add(newstorevalue["itemName"].ToString());
testList.Add(newstorevalue["itemLocation"].ToString());
testList.Add(newstorevalue["itemChBy"].ToString());
}
}
}
You can try with this approach, I will use Data Transfer Object for holding data retrieved from database and Execute DataReader for reading.
First of all, you need to create a DTO class, I will call it LightItemDTO
public class LightItemDTO
{
public int Id { get; set; }
public string Name { get; set; }
public string Location { get; set; }
public string ChangedBy { get; set; }
}
Note: How to know the type of properties, you can reference this link: SQL Server Data Type Mappings
And now, I will using ADO.NET for execute the stored procedure to get data from database
public IEnumerable<LightItemDTO> GetLightItem(string itemText, string sqlConnectionString)
{
var results = new List<LightItemDTO>();
using (var con = new SqlConnection(sqlConnectionString))
{
using (var cmd = new SqlCommand("sp_lightItem", con))
{
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add("#ItemID", SqlDbType.VarChar).Value = itemText;
con.Open();
using (var reader = cmd.ExecuteReader())
{
results.Add(new LightItemDTO
{
Id = Convert.ToInt32(reader["itemID"]),
Name = reader["itemName"].ToString(),
Location = reader["itemLocation"].ToString(),
ChangedBy = reader["itemChBy"].ToString()
});
}
}
}
return results;
}
Using DataReader is the best practice with high performance.
ADO.NET is the manual way to achieve this task, you can use some ORM framework for do it easier, such as: Entity Framework, Dapper.NET ...
You could execute stored procedure with parameters in following:
using (SqlConnection con = new SqlConnection(dc.Con)) {
using (SqlCommand cmd = new SqlCommand("sp_lightItem", con)) {
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add("#ItemID", SqlDbType.VarChar).Value = itemId.Text;
con.Open();
cmd.ExecuteNonQuery();
}
}
first of all set the Commandtype as stored procedure and that procedure will return some data which you will save in dataset and then return the data set to where ever you want to populate the data
public DataSet LoadItemNew(int ItemID)
{
var acf = new AcFunctions();
return DataSet ds = SqlHelper.ExecuteDataset(acf.AcConn(),CommandType.StoredProcedure, "sp_lightItem",new SqlParameter("#itemID" ItemID);
}
You can try like this ..
public string LoadItemNew(int ItemID)
{
var acf = new AcFunctions();
List<SqlParameter> parameters = new List<SqlParameter>();
parameters.Add(new SqlParameter("#itemID", ItemID));
DataSet Ds = SqlHelper.ExecuteDataset(acf.AcConn(),CommandType.StoredProcedure, "sp_lightItem" , parameters.ToArray());
return "ok";
}

C# - System Data SqlClient SqlException

So I am in the midst of debugging a program I have been working on quite sometime that will email the user an email that will the user an email that contains information on an error a client is having trying to get into a mobile app. The problem that I am having is even though I have set the parameter, I am getting the following error message:
The parameterized query '(#USER_NAME varchar(8000))SELECT USER_NAME, EMAIL, FIRST_NAME, L' expects the parameter '#USER_NAME', which was not supplied.
I have never encountered this error before so I am not sure if there is something I am missing in my code or if I just put in the wrong syntax. Below is both the method where the error is happening in and the class that this method uses:
Method:
private static MyHomeInformation GetUserDataFromMyHome(string username)
{
MyHomeInformation myHomeInformation = null;
using (SqlConnection connection = new SqlConnection(Properties.Settings.Default.MyHomeConnectionString))
{
SqlCommand sqlError = connection.CreateCommand();
sqlError.CommandText = #"SELECT USER_NAME, EMAIL, FIRST_NAME, LAST_NAME, TRAVELER_UID FROM TANDT_PORTAL.dbo.[USER] WHERE USER_NAME = #USER_NAME";
sqlError.Parameters.Add("#USER_NAME", System.Data.SqlDbType.VarChar);
connection.Open();
SqlDataReader reader = sqlError.ExecuteReader();
if (reader.Read())
{
myHomeInformation = new MyHomeInformation();
myHomeInformation.myHomeUserName = Utilities.FromDBValue<string>(reader["USER_NAME"]);
myHomeInformation.myHomeEmail = Utilities.FromDBValue<string>(reader["EMAIL"]);
myHomeInformation.myHomeFirstName = Utilities.FromDBValue<string>(reader["FIRST_NAME"]);
myHomeInformation.myHomeLastName = Utilities.FromDBValue<string>(reader["LAST_NAME"]);
myHomeInformation.myHomeTravelerUID = Utilities.FromDBValue<Guid>(reader["TRAVELER_UID"]);
}
}
return myHomeInformation;
}
Class:
class MyHomeInformation
{
public string myHomeUserName { get; set; }
public string myHomeEmail { get; set; }
public string myHomeFirstName { get; set; }
public string myHomeLastName { get; set; }
public Guid myHomeTravelerUID { get; set; }
}
Change the sqlError.Parameters.Add to AddWithValue
private static MyHomeInformation GetUserDataFromMyHome(string username)
{
MyHomeInformation myHomeInformation = null;
using (SqlConnection connection = new SqlConnection(Properties.Settings.Default.MyHomeConnectionString))
{
SqlCommand sqlError = connection.CreateCommand();
sqlError.CommandText = #"SELECT USER_NAME, EMAIL, FIRST_NAME, LAST_NAME, TRAVELER_UID FROM TANDT_PORTAL.dbo.[USER] WHERE USER_NAME = #USER_NAME";
sqlError.Parameters.AddWithValue("#USER_NAME", username); // THIS ROW SHOULD BE UPDATED
connection.Open();
SqlDataReader reader = sqlError.ExecuteReader();
if (reader.Read())
{
myHomeInformation = new MyHomeInformation();
myHomeInformation.myHomeUserName = Utilities.FromDBValue<string>(reader["USER_NAME"]);
myHomeInformation.myHomeEmail = Utilities.FromDBValue<string>(reader["EMAIL"]);
myHomeInformation.myHomeFirstName = Utilities.FromDBValue<string>(reader["FIRST_NAME"]);
myHomeInformation.myHomeLastName = Utilities.FromDBValue<string>(reader["LAST_NAME"]);
myHomeInformation.myHomeTravelerUID = Utilities.FromDBValue<Guid>(reader["TRAVELER_UID"]);
}
}
return myHomeInformation;
}
Here is an example of the correct way to use the parameters collection add function:
SqlParameter param = new SqlParameter("#USER_NAME", SqlDbType.NVarChar, 8000);
param.Value = "Sam";
sqlError.Parameters.Add(param);
Note the setting of the value to Sam.
You could also use the function AddWithValue if you don't want to create the param variable. (However, #JoelCoehoorn will be worried about performance issues - see question comments)
You add your parameter to the call, but give the parameter no value.
change
sqlError.Parameters.Add("#USER_NAME", System.Data.SqlDbType.VarChar);
to
sqlError.Parameters.Add("#USER_NAME", System.Data.SqlDbType.VarChar).value = username;

Return multiple values from a SELECT query

For example i have a database called dbuser:
username: teste
password: xxxx
isonline: 1
username: teste2
password: xxxx
isonline: 1
I thought that this query:
"SELECT username FROM dbuser WHERE (isonline ='1')"
would return both teste and teste2, but when i ask the result for example in a MessageBox, with both teste and teste2 online, it only shows teste, but when i close the teste connection then it appears teste2 in the MessageBox. Im guessing its only returning the first row to me, so how can i get all the values?
This is the method code:
public static string GetOnline()
{
string listaOnline;
listaOnline = ExecuteQuery("SELECT * username FROM dbuser WHERE (isonline ='1')").ToString();
return listaOnline;
}
and I show it as MessageBox.Show(DbManager.GetOnline());
This should give you a list of strings that you want in the fastest manner. reader.GetString(0) means that you take a sting value from column with index 0 (so the first one).
List<string> result = new List<string>();
using (SqlConnection connection = new SqlConnection(databaseConnectionString))
{
connection.Open();
using (SqlCommand command = new SqlCommand(query, connection))
{
command.CommandType = CommandType.Text;
using (SqlDataReader reader = command.ExecuteReader())
{
while (reader.Read())
{
result.Add(reader.GetString(0));
}
reader.Close();
}
command.Cancel();
}
}
return result;
Try something like the following, create a class for users:
[Table(Name = "foo")]
public class Users
{
[Column(Name = "user_name")]
username {get; set;};
[Column(Name = "user_pword")]
password {get; set;};
[Column(Name = "user_isonline")]
isonline {get; set;};
}
public static List<Users> GetOnline()
{
List<Users> listaOnline = new List<Users>();
listaOnline = ExecuteQuery("SELECT * username FROM dbuser WHERE (isonline ='1')");
return listaOnline;
}
You can do something like this, it is just outline of the code, will give you some hint(need to add some spaces/ special characters to between names).
string user = string.Empty;
foreach (var item in DbManager.GetOnline())
{
user += item;
}
MessageBox.Show(user);
Hope it will help..
Also you can do it in the following way:
List<string> listUser= new List<string>();
listUser =DbManager.GetOnline();
string users= string.Join(",", listUser.ToArray());
MessageBox.Show(users);
In this way Username will be printed separated by ",".

Categories

Resources