put values from Inner join in object that is in an object - c#

I have the following sql command with an Inner join:
SqlCommand cmd = new SqlCommand(#"SELECT c.comment_Id, c.date, c.comment, c.rating, a.registration_Date, a.username, a.email, a.profile_Image FROM News_comments c INNER JOIN News_accounts a ON c.account_Id=a.account_Id WHERE c.news_Id = #news_Id", conn);
cmd.Parameters.Add("news_Id", SqlDbType.Int).Value = news_Id;
conn.Open();
I want to put the values from a (News_accounts) in the object account that is in itself in the object newsComment which is located in a generic List, List newsComments.
I do that like this:
using (SqlDataReader reader = cmd.ExecuteReader()) {
while (reader.Read()) {
Comments newsComment = new Comments();
newsComment.comment_Id = int.Parse(reader["comment_Id"].ToString());
newsComment.date = DateTime.Parse(reader["date"].ToString());
newsComment.comment = reader["comment"].ToString();
newsComment.rating = int.Parse(reader["rating"].ToString());
newsComment.account.Registration_Date = DateTime.Parse(reader["registration_Date"].ToString());
newsComment.account.Username = reader["username"].ToString();
newsComment.account.Email = reader["email"].ToString();
newsComment.account.Profile_Image = reader["profile_Image"].ToString();
newsComments.Add(newsComment);
}
return newsComments;
}
Comments has a constructor:
public Comments(int comment_Id, DateTime date, string comment, int rating, Accounts account) {
this.comment_Id = comment_Id;
this.date = date;
this.comment = comment;
this.rating = rating;
this.account = account;
}
And Accounts account as well:
public Accounts(int account_Id, DateTime registration_Date, string email, string username, string profile_Image, string homepage, string about, bool admin) {
this.account_Id = account_Id;
this.registration_Date = registration_Date;
this.email = email;
this.profile_Image = profile_Image;
this.homepage = homepage;
this.about = about;
this.admin = admin;
}
Up until rating all goes well and the values are being put in the newsComment object, however, when it reaches the values that need to be put in the object account that is located in the object newsComment, it gives a NullReferenceException.
I know this means that it doesn't have a value but I can't seem to find why it has no value.
I've looked checked my Inner join with sql server 2008 query designer, and that works
so it's got to be the object but I don't see the problem.
please help me :)
greetings

newsComment.account You have to initialize that object before accessing its fields, properties, or methods. Something like this:
newsComment.account = new Account();
newsComment.account.Registration_Date = DateTime.Parse(reader["registration_Date"].ToString());
newsComment.account.Username = reader["username"].ToString();
newsComment.account.Email = reader["email"].ToString();
newsComment.account.Profile_Image = reader["profile_Image"].ToString();
... or you can do it from the constructor of the Comments class, the one which takes no arguments.
As a side note: maybe you should consider using an ORM, like LINQ to SQL or Entity Framework. It's what they do.

You are not instantiating your account object so it is null

Related

How do I display the data of the "current user"?

I am a high school student who's still pretty much a beginner in C#.
I am making a library management system (for books) that includes a database (sql local database in visual studio(?)) for users. I have a form wherein users can view the data they have input in the registration form (userID, name, username, course, section). The only problem is that it only displays the data of the first account created. No matter how many other accounts I create, it still only ever displays the first one. How do I make it so that it shows the data of the "current" user/account logged in?
I've tried slightly changing the code by changing
SqlCommand cmd = conn.CreateCommand();
cmd.CommandType = CommandType.Text;
cmd.CommandText = "Select * from [tbl_accounts]";
into
string select = "Select * from [tbl_accounts]";
SqlCommand cmd = new SqlCommand(select, conn);
Although, I think they're basically the same. I don't really know what to do since the other solutions I've found are much more complex.
This is the code that I am using right now:
try
{
SqlConnection conn = new SqlConnection(#"[connection string]");
conn.Open();
string select = "Select * from [tbl_accounts]";
SqlCommand cmd = new SqlCommand(select, conn);
SqlDataReader dr = cmd.ExecuteReader();
if(dr.Read())
{
materialLabel6.Text = dr["accountID"].ToString();
materialLabel7.Text = dr["username"].ToString();
materialLabel8.Text = dr["name"].ToString();
materialLabel9.Text = dr["strand"].ToString();
materialLabel10.Text = dr["section"].ToString();
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);}
}
The outcome that I would like to see is for example:
Users(table):
PersonA
PersonB
Currently Logged in: PersonB
[PERSONB'S DATA]
So it means that the form will only display the data of PersonB instead of PersonA's
For starters, if you need more than one row of data, you'll want to loop through all the rows in the data reader. Right now you're only getting the first row returned. This link should have the relevant information for that. However, ideally, you'd want to send a parameter from the UI (or whatever it is that you're using to fire off the call to the function) that denotes the user (an ID or any unique field in the Users table) and send that to the sql query's where clause so you only pull the record(s) that you need.
The query should probably look something like:
public void GetUserInfo(int userId) // though you might want to change the parameter to suit your needs. It's hard to tell without being able to see the schema
{
string select = string.Format("Select * from [tbl_accounts] where Id = {0}", userId.ToString()); // something along these lines
SqlCommand cmd = new SqlCommand(select, conn);
SqlDataReader dr = cmd.ExecuteReader();
if(dr.Read())
{
materialLabel6.Text = dr["accountID"].ToString();
materialLabel7.Text = dr["username"].ToString();
materialLabel8.Text = dr["name"].ToString();
materialLabel9.Text = dr["strand"].ToString();
materialLabel10.Text = dr["section"].ToString();
}
}
Edit: quick note, if you adjust your query so it pulls one record based off of a parameter, you shouldn't need to do the looping.
Another quick edit: I broke up the code so it's a little more readable. This is more of an 'ideal implementation,' and enforces some better practices for code. (I know it's a high school project, but it's best to get used to breaking up code so it's more generic early on imo. This is mostly for maintainability. On larger projects keeping everything so closely coupled together is hard to manage.)
public User GetUserInfo(int userId) // though you might want to change the parameter to suit your needs. It's hard to tell without being able to see the schema for the user table
{
SqlConnection conn = new SqlConnection(#"[connection string]");
conn.Open();
string select = string.Format("Select * from [tbl_accounts] where Id = {0}", userId.ToString()); // something along these lines
SqlCommand cmd = new SqlCommand(select, conn);
SqlDataReader dr = cmd.ExecuteReader();
User user = new User();
if(dr.Read())
{
user.AccountId = dr["accountID"].ToString();
user.UserName = dr["username"].ToString();
user.Name = dr["name"].ToString();
user.Strand = dr["strand"].ToString();
user.Section = dr["section"].ToString();
}
return user;
}
public void SetValues(User user)
{
materialLabel6.Text = user.AccountId;
materialLabel7.Text = user.UserName;
materialLabel8.Text = user.Name;
materialLabel9.Text = user.Strand;
materialLabel10.Text = user.Section;
}
public class User
{
string AccountId { get; set; }
string UserName { get; set; }
string Name { get; set; }
string Strand { get; set; }
string Section { get; set; }
}

MVC multiple results from SQL database not working correctly

Good morning,
I have been learning MVC .NET over the weekend and I'm trying to get data from a database but the results are not displaying:
I am using the following code:
public class ListClientsController : Controller
{
private string QueryString;
private string myConnectionString;
public List<ListClients> models = new List<ListClients>();
public ActionResult Index()
{
bool iterate = true;
string iname = "";
string isurname = "";
int iIDNO = 0;
QueryString = "select FirstName, Surname, IDNumber from [iDtB].[dbo].[Clients]";
myConnectionString = "Data Source=<name>\\SQLEXPRESS01;Integrated Security=SSPI";
SqlConnection iDtBData = new SqlConnection(myConnectionString);
iDtBData.Open();
SqlCommand SQLCMD = new SqlCommand(QueryString, iDtBData);
SqlDataReader Reader = SQLCMD.ExecuteReader();
Reader.Read();
while (iterate)
{
try
{
iname = Reader["FirstName"].ToString();
isurname = Reader["Surname"].ToString();
int.TryParse(Reader["IDNumber"].ToString(), out iIDNO);
models.Add(new ListClients
{
Name = iname,
Surname = isurname,
IDNO = iIDNO
});
Reader.NextResult();
}
catch
{
iterate = false;
}
}
Reader.Close();
iDtBData.Close();
return View(models);
}
}
For some reason I get this result:
MVC Client results
Now notably, I understand that I am getting "0" for ID number due to mismatching fields, I don't understand why I can't get multiple results to display.
Thanks in advance!
You aren't actually reading all of the results returned from the query. NextResult() returns the next result set, not the next record. Replace it with Read() and you should start getting all of the results.
I'd also eliminate the Try/Catch loop as that will prevent you from seeing any errors that do occur. Plus, Read returns a true/false to let you know if it read a record or not making eliminating it easy.
while (Reader.Read())
{
try
{
iname = Reader["FirstName"].ToString();
isurname = Reader["Surname"].ToString();
int.TryParse(Reader["IDNumber"].ToString(), out iIDNO);
models.Add(new ListClients
{
Name = iname,
Surname = isurname,
IDNO = iIDNO
});
}
I'd also recommend taking a look at how to use the using statement (https://msdn.microsoft.com/en-us/library/yh598w02.aspx) to improve handling of the connection and the data reader.

Specified cast is not valid. SQL Parameter

I keep getting back specified cast is not valid on the int result = myDataReader.GetInt32(0); line when running the query using the parameter.
The thing is if I replace #Reg with text 'WM07 OGR' it works fine. However the string reg returns this so why the error?
string reg = RadComboBox1.SelectedValue;
//prepare sql statements
Command = new OleDbCommand(#"SELECT MAX(Fuel.NO_ODOMETER_LAST) AS PrevMiles
FROM (Fuel INNER JOIN Vehicle ON Fuel.TX_VIN = Vehicle.TX_VIN)
WHERE (Vehicle.TX_VEHNUMBER = '#Reg')", conn);
Command.Parameters.AddWithValue("#Reg", OleDbType.WChar);
Command.Parameters["#Reg"].Value = reg;
myDataReader = Command.ExecuteReader();
if (myDataReader.Read())
{
int result = myDataReader.GetInt32(0);
Prev_Mileage.Text = result.ToString();
}
//cleanup objects
myDataReader.Close();
conn.Close();
The thing is if I replace #Reg with text 'WM07 OGR' it works fine.
However the string reg returns this so why the error?
It seems that you get the error if the query returns null because there is no matching TX_VEHNUMBER, then the cast to int fails.
So you have to check if it's null:
int result = 0; // insert default
if(!myDataReader.IsDbNull(0))
result = myDataReader.GetInt32(0)
Apart from that it doesn't work because your parameter is interpreted as value, you have wrapped it in apostrophes here:
WHERE (Vehicle.TX_VEHNUMBER = '#Reg')
You just have to do this:
WHERE (Vehicle.TX_VEHNUMBER = #Reg)
Try some thing like this.
Command = new OleDbCommand(#"SELECT MAX(Fuel.NO_ODOMETER_LAST) AS PrevMiles
FROM (Fuel INNER JOIN Vehicle ON Fuel.TX_VIN = Vehicle.TX_VIN)
WHERE (Vehicle.TX_VEHNUMBER = #Reg)", conn);
Or
Command = new OleDbCommand(#"SELECT MAX(Fuel.NO_ODOMETER_LAST) AS PrevMiles
FROM (Fuel INNER JOIN Vehicle ON Fuel.TX_VIN = Vehicle.TX_VIN)
WHERE (Vehicle.TX_VEHNUMBER = '?')", conn);
Or
Command = new OleDbCommand(#"SELECT MAX(Fuel.NO_ODOMETER_LAST) AS PrevMiles
FROM (Fuel INNER JOIN Vehicle ON Fuel.TX_VIN = Vehicle.TX_VIN)
WHERE (Vehicle.TX_VEHNUMBER = ?)", conn);
For more reference see following links.
http://www.java2s.com/Code/CSharp/Database-ADO.net/PassparametertoOleDbCommand.htm
http://blogs.msdn.com/b/wriju/archive/2008/01/24/ado-net-oledbcommand-parameterized-query-sequence-rule.aspx
Since it's getting into the below block...
if (myDataReader.Read())
{
int result = myDataReader.GetInt32(0);
Prev_Mileage.Text = result.ToString();
}
...I assume you have a record. I'd check the DataType of NO_ODOMETER_LAST, as it might be a varchar or something other than an int. If thats a case, you might need to use a TryParse.

DataReader IndexOutofRangeException was unhandled by user code

I ran into another issue again. I was trying to get data from the database using DataReader but I got the error when i was testing my code. Can anyone help me out? The error occurred at this line:
chkAssess = readAssess[columnName].ToString();
Below is the code snippet:
public string CheckAssess(string emailAddress, string columnName)
{
string chkAssess = "";
SqlDataReader readAssess;
//readAssess = new SqlDataReader();
string MgrAssessQry = "SELECT '"+columnName+"' FROM tblAllUsers";
//MgrAssessQry += " WHERE email ='" + emailAddress + "'";
SqlCommand cmdReadAssess = new SqlCommand(MgrAssessQry, cn);
cn.Open();
readAssess = cmdReadAssess.ExecuteReader();
while(readAssess.Read())
{
// Add the rows
chkAssess = readAssess[columnName].ToString();
}
return chkAssess;
}
try to use column name without ''
select something from table
instead of
select 'something' from table
for security reasons, don't create sql queries in that way (by concatenating strings) - use #parameters instead
2. close the reader at the end
Try this:
public string CheckAssess(string emailAddress, string columnName)
{
string chkAssess = "";
SqlDataReader readAssess;
//readAssess = new SqlDataReader();
string MgrAssessQry = "SELECT #Column_Name FROM tblAllUsers";
SqlCommand cmdReadAssess = new SqlCommand(MgrAssessQry, cn);
cmdReadAssess.Parameters.AddWithValue(new SqlParameter("Column_Name", columnName));
cn.Open();
readAssess = cmdReadAssess.ExecuteReader();
while(readAssess.Read())
{
// Add the rows
chkAssess = readAssess.GetString(0);
}
return chkAssess;
}
You have got several problems here.
Check whether your readAssess has rows like below.
if(readAssess.HasRows)
If it doesn't have rows then trying
chkAssess = readAssess.GetString(0);
would throw this error, as Arrays are index-based.
So your code should be like below
if(readAssess.HasRows)
{
while(readAssess.Read())
{
chkAssess = readAssess.GetString(0);
}
}
Other problem is you need to close both the reader & the connection afterwards.
readAssess.Close();
cn.Close();
Also your code is potentially vulnerable to SQL Injection.
if (reader.HasRows)
{
while (reader.Read())
{
int result = Convert.ToInt32(reader.GetString(0));
Console.WriteLine(result);
}
}
The most important thing is check the query first by executing in SQL Server and see if any result is coming or not.
Secondly based on the type of output you are receiving cast it to that particular data type (important).Mostly everyone is saving the data in varchar so.

How to make Sqlcommand accept null values

I'm trying to get data in a gridview from a database to show up in text boxes upon clicking and it works fine for the rows with no null data, although since my int columns have some null values my GetInt32 methods keep returning "Data is Null. This method or property cannot be called on Null values."
Is there a simple way to fix or work around this? Do I replace GetInt32 with another method? I'd like for the data that is null to show up blank/empty in the text boxes if possible. Here's my code if you have any suggestions, thanks.
public ArrayList GetAllPersonnel(int WorkerID) {
using (var connection = new SqlConnection(connectionString)) {
connection.Open();
String query = "Select * FROM Personnel WHERE WorkerID = " + WorkerID;
using (var command = new SqlCommand(query, connection)) {
var reader = command.ExecuteReader();
var list = new ArrayList();
while (reader.Read()) {
String firstname = reader.GetString(1);
String lastname = reader.GetString(2);
String occupation = reader.GetString(3);
String deployment = reader.GetString(4);
int disasterid = reader.GetInt32(5);
String location = reader.GetString(6);
int deployedhours = reader.GetInt32(7);
int resthours = reader.GetInt32(8);
list.Add(firstname);
list.Add(lastname);
list.Add(occupation);
list.Add(deployment);
list.Add(disasterid);
list.Add(location);
list.Add(deployedhours);
list.Add(resthours);
}
connection.Close();
reader.Close();
return list;
}
}
}
You should use IsDBNull method of the SqlDataReader
int resthours = (!reader.IsDBNull(8) ? reader.GetInt32(8) : 0);
or, more directly
list.Add((!reader.IsDBNull(8) ? reader.GetInt32(8).ToString(): string.Empty));
Said that, I have noticed that you use a string concatenation to build the sql command text to retrieve records. Please do not do that. It is very dangerous and could lead to Sql Injection
String query = "Select * FROM Personnel WHERE WorkerID = #wkID";
using (var command = new SqlCommand(query, connection))
{
command.Parameters.AddWithValue("#wkID", WorkerID);
var reader = command.ExecuteReader();
....
OK, so you're effectively saying that everything you display should be a string type, which is fine, I'm just making that point because you stated you want even integers to show up as an empty string. So how about this code?
String firstname = reader.GetString(1);
String lastname = reader.GetString(2);
String occupation = reader.GetString(3);
String deployment = reader.GetString(4);
String disasterid = reader.IsDBNull(5) ? string.Empty : reader.GetString(5);
String location = reader.GetString(6);
String deployedhours = reader.IsDBNull(7) ? string.Empty : reader.GetString(7);
String resthours = reader.IsDBNull(8) ? string.Empty : reader.GetString(8);
list.Add(firstname);
list.Add(lastname);
list.Add(occupation);
list.Add(deployment);
list.Add(disasterid);
list.Add(location);
list.Add(deployedhours);
list.Add(resthours);
Now, the reason I stated that you want to leverage everything as a string is because the default value for a int is 0 and that wouldn't meet the empty text box requirement.
You have at least two ways to sort this out
Modify your sql to select either zero or whatever you think suitable in the place of null value. This will ensure that you always have an integer value in the integer column. It can be done in the following manner
select ISNULL ( ColumnName , 0 ) as ColumnName from xxx
Always fetch object from the reader and check if it is null or not. If it is null then replace it with suitable value.

Categories