Small brief of what i need and what i currently have
I connect to a database and get my data from it and i get ( Name , LongNumber) and basically i have an event (action) that fires when the event occurs (this action gives me a LongNumber aswell).
I need to compare the LongNumbers between the one i got from the event (action) and the one from my database, and if they are similar i take the name and use it.
for example Hello, Alex ( alex is taken from the database)
Issue
I get Index out of range, and using my logic all texts will change what i'm trying to achieve is to change the text to the name of the person that got the Long number the same as the longNumber from the event
Code: Gettings Data from the database
using UnityEngine;
using System.Collections;
using MySql.Data.MySqlClient;
using System;
using System.Linq;
using System.Collections.Generic;
public class MySqlTestScript : MonoBehaviour
{
private static MySqlTestScript _instnace;
public static MySqlTestScript sharedInstance()
{
return _instnace;
}
string row = "";
public string host = "*****";
public string database = "*******";
public string usrename= "*******";
public string password = "******";
public List<Data> userData = new List<Data>();
Data data;
void Awake()
{
_instnace = this;
}
// Use this for initialization
public void Start()
{
GetDataFromDatabase();
}
public string GetDataFromDatabase()
{
string myConnectionString = "Server="+host+";Database="+database+";Uid="+usrename+ ";Pwd="+password+";";
MySqlConnection connection = new MySqlConnection(myConnectionString);
MySqlCommand command = connection.CreateCommand();
command.CommandText = "SELECT * FROM Users";
MySqlDataReader Reader;
try
{
connection.Open();
Reader = command.ExecuteReader();
while (Reader.Read())
{
for (int i = 0; i < Reader.FieldCount; i++)
{
//rfid_tags.Add (Reader.GetString("UserName"));
//rfid_tags.Add(Reader.GetString("RFID_Tag"));
data = new Data(Reader.GetString("UserName"), Reader.GetString("RFID_Tag"));
userData.Add(data);
// ws.DomainUrl = reader.GetString("DomainUrl");
// rfid_tags.Add(Reader.GetValue(i).ToString() + ",");
// row += Reader.GetValue(i).ToString() + ", ";
}
Debug.Log(row);
}
}
catch (Exception x)
{
Debug.Log(x.Message);
return x.Message;
}
connection.Close();
return row;
}
}
public class Data {
public string username { get; set; }
public string rfid { get; set; }
public Data(string _name, string _rfid)
{
username = _name;
rfid = _rfid;
}
public void SetUserName(string _userName) { username = _userName; }
public void SetRFID(string _rfid) { rfid = _rfid; }
}
Code for Comparing the values from the event(action) and showing the text
void OnTagsReported(ImpinjReader sender, TagReport report)
{
Debug.Log("OnTagsReported");
// This event handler is called asynchronously
// when tag reports are available.
// Loop through each tag in the report
// and print the data.
foreach (Tag tag in report)
{
Debug.Log(tag.Epc);
// Debug.Log(MySqlTestScript.sharedInstance().rfid_tags[0]);
Debug.Log("STEP ONE");
for (int i = 0; i < MySqlTestScript.sharedInstance().userData.Count; i++)
{
Debug.Log("STEP TWO");
if (tag.Epc.ToString().Trim() == MySqlTestScript.sharedInstance().userData[i].rfid)
{
Debug.Log("STEP THREE");
// TODO References the Name
Loom.QueueOnMainThread(() => {
namesTxt[i].text = MySqlTestScript.sharedInstance().userData[i].username;
});
}
}
As you can see in the Event script it's so unflexible and it gives index out of range if my database has less than 6 rows. I need to make it more friendly and generic
Any help would be appreciated and i hope my question is clear Thank you :)!
This should make more sense:
Database:
using UnityEngine;
using System.Collections;
using MySql.Data.MySqlClient;
using System;
using System.Linq;
using System.Collections.Generic;
public class MySqlTestScript : MonoBehaviour
{
private static MySqlTestScript _instnace;
public static MySqlTestScript sharedInstance()
{
return _instnace;
}
string row = "";
public string host = "*****";
public string database = "*******";
public string usrename= "*******";
public string password = "******";
public List<AppUser> users = new List<AppUser>();
void Awake()
{
_instnace = this;
}
// Use this for initialization
public void Start()
{
GetDataFromDatabase();
}
public string GetDataFromDatabase()
{
string myConnectionString = "Server=" + host + ";Database=" + database + ";Uid=" + usrename + ";Pwd=" + password + ";";
MySqlConnection connection = new MySqlConnection(myConnectionString);
MySqlCommand command = connection.CreateCommand();
command.CommandText = "SELECT * FROM users";
MySqlDataReader Reader;
try
{
connection.Open();
Reader = command.ExecuteReader();
while (Reader.Read())
{
users.Add(new AppUser() {
username = Reader.GetString("UserName").Trim(),
rfid = Reader.GetString("longNumbers ").Trim()
});
}
}
catch (Exception x)
{
Debug.Log(x.Message);
return x.Message;
}
connection.Close();
return row;
}
}
Data object:
public Class AppUser
{
public string rfid { get; set; }
public string username { get; set; }
}
Event/data comparison:
void OnTagsReported(ImpinjReader sender, TagReport report)
{
Debug.Log("OnTagsReported");
// This event handler is called asynchronously
// when tag reports are available.
// Loop through each tag in the report
// and print the data.
foreach (Tag tag in report)
{
Debug.Log(tag.Epc);
List<AppUser> appUsers = MySqlTestScript.sharedInstance().users;
int numUsers = appUsers.Count;
Debug.Log(numUsers);
for (int i = 0; i < numUsers; i++)
{
if (tag.Epc.ToString().Trim() == appUsers[i].rfid)
{
// TODO References the Name
Loom.QueueOnMainThread(() => {
if (i < namesTxt.Count) namesTxt[i].Text = appUsers[i].username; //assumes textnames is a "List" of textboxes and is already populated with instances of text1, text2 text3 etc. The if is just to guard against there being more rows in the DB than textboxes
});
}
}
}
}
One way to get around this is to wrap each of your cases in another if statement to check if the database has that many rows
if (MySqlTestScript.sharedInstance().longNumbers.Length > 1) {
if (tag.Epc.ToString().Trim() == MySqlTestScript.sharedInstance().longNumbers[0].ToString()) {
if(MySqlTestScript.sharedInstance().userNames[0] != null)
txt1.text = "Hello "+ MySqlTestScript.sharedInstance().userNames[0];
}
}
}
else {
txt1.text = "";
}
This will avoid your index out of range exception. In each wrapping case you will need to increment the comparison to be 1, 2, 3, etc.
It is not the prettiest solution but will avoid the exception. Another option would be to wrap in a try-catch block. Again not the prettiest but would accomplish the task.
Related
ERROR:
System.FormatException: 'Input string was not in a correct format.'
I am having the user input into a form, but before it is accepted it must be validated (starting with IsFilledIn()). I have parsed the string from the textbox in the form to an int so it can go into the DB. It seems it is completely skipping validation when I hit submit but have included all that is need (I think).
Current code:
Form1:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using Lab5_validation;
using System.Data.SqlClient;
namespace FinalAJG
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void btnsubmit_Click(object sender, EventArgs e)
{
Character temp = new Character();
//Sets temporary Variables
temp.CharName = txtCharName.Text;
temp.Health = Int32.Parse(txtHealth.Text);
temp.Lvl = Int32.Parse(txtLvl.Text);
temp.Agility = Int32.Parse(txtAgility.Text);
temp.Strength = Int32.Parse(txtStrength.Text);
temp.Stamina = Int32.Parse(txtStamina.Text);
temp.Armor = Int32.Parse(txtArmor.Text);
temp.HoursPlayed = Double.Parse(txtHoursPlayed.Text);
temp.PlayedSince = DateTime.Parse(txtPlayedSince.Text);
temp.Cass = txtClass.Text;
if (!temp.Feedback.Contains("ERROR:"))
{
Feedback.Text = temp.AddARecord();
}
else
{
Feedback.Text = temp.Feedback;
}
}
}
}
Character.cs:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Data;
using System.Data.SqlClient;
using Lab5_validation;
namespace FinalAJG
{
class Character
{
//Define PRIVATE variables.
bool blnResult;
private string charname;
private string cass;
private int health;
private int lvl;
private int agility;
private int strength;
private int stamina;
private int armor;
private double hoursplayed;
private DateTime playedsince;
private float AvgPlayTime=777;
private int TotalPoints=444;
public string feedback = "";
// Creates Public variables the users can input to, then stores them to private variable if validated correctly.
public string CharName
{
get
{
//Returns private variable to class.
return charname;
}
set
{
//IF-ELSE statement to validate user input.
if (ValidationLibrary.IsItFilledIn(value))
{
// Sets private variable 'fName' to equal user input on public variable.
charname = value;
}
else
{
//If they enter invalid input, the feed back will be set to an error message, and the private variable will not be set.
feedback += "\n\nERROR: Enter your charcters name!!!";
}
}
}
//Public variable to receive user input securly.
public string Cass
{
get
{
//Returns Private variable
return cass;
}
set
{ //IF-ELSE statement to validate user input.
if (ValidationLibrary.IsItFilledIn(value))
{
cass = value; // Sets Private Variable
}
else
{//Feedback gives error IF validation
feedback += "\n\nERROR: Enter your Class!(ONLY OPTIONS ARE, Mage, Assassin, Warrior, Archer, Druid, and Warlock!!";
}
}
}
//Same as previous Public Variables
public int Health
{
get
{
return health;
}
set
{ //Checks for user input, if not true give error feedback.
if (ValidationLibrary.IsItFilledIn(value.ToString()))
{
health = value;
}
else
{
feedback += "\n\nERROR: Enter Characters Health (25-100)!! ";
}
}
}
//Same as previous Public Variables
public int Lvl
{
get
{
return lvl;
}
set
{
if (ValidationLibrary.IsItFilledIn(value.ToString()))
{
lvl = value;
}
else
{
feedback += "\n\nERROR:Enter your Characters level! (1-80)\n ";
}
}
}
//Same as previous Public Variables, except it will not be required to fill it in.
public int Agility
{
get
{
return agility;
}
set
{
if (ValidationLibrary.IsItFilledIn(value.ToString()))
{
agility = value;
}
else
{
feedback += "\n\nERROR:Enter valid Date!! (02-20-2020, 11/11/2011, 06/24/2019)";
}
}
}
//Same as previous Public Variables
public int Strength
{
get
{
return strength;
}
set
{
//Validates to ensure the required length of zipcode is entered
if (ValidationLibrary.IsItFilledIn(value.ToString()))
{
do
{
strength = value;
} while (blnResult == true);
}
else
{
feedback += "\n\nERROR:Enter Characters strength!! (1-25) ";
}
}
}
//Same as previous Public Variables
public int Stamina
{
get
{
return stamina;
}
set
{
if (ValidationLibrary.IsItFilledIn(value.ToString()))
{
stamina = value;
}
}
}
//Same as previous Public Variables except the validation checks for valid characters and the correct length
public int Armor
{
get
{
return armor;
}
set
{
if (ValidationLibrary.IsItFilledIn(value.ToString()))
{
armor = value;
}
else
{
feedback += "\n\nERROR:Enter Valid armor rating!! (10-50) ";
}
}
}
//Same as previous Public Variables
public double HoursPlayed
{
get
{
return hoursplayed;
}
set
{
if (ValidationLibrary.IsItFilledIn(value.ToString()))
{
hoursplayed = value;
}
else
{
feedback += "\n\nERROR:Enter Valid Number!!(.5, 2.3, 6.4, 92)";
}
}
}
//Same as previous Public Variables except checks for required characters for a valid Email.
public DateTime PlayedSince
{
get
{
return playedsince;
}
set
{
if (ValidationLibrary.IsItFilledIn(value.ToString()))
{
playedsince = value;
}
else
{
feedback += "\n\nERROR:Enter valid Date!! (02-20-2020, 11/11/2011, 06/24/2019)";
}
}
}
// Feedback variable to display results or error message.
public string Feedback
{
get
{
return feedback;
}
set
{ // IF feedback contains 'ERROR' then leave it blank and display which input was incorrect
if (feedback.Contains("ERROR:"))
{
feedback += "";
}
//ELSE, store and display the results.
else
{
feedback = value;
}
}
}
public string AddARecord()
{
//Init string var
string strResult = "";
//Make a connection object
SqlConnection Conn = new SqlConnection();
//Initialize it's properties
Conn.ConnectionString = ********************";//Set the Who/What/Where of DB
//Sends command to SQL Server
string strSQL = "INSERT INTO Chars (CharName, Class, Health, Lvl, Agility, Strength, Stamina, Armor, HoursPlayed, PlayedSince, AvgPlayTime, TotalPoints) VALUES (#CharName, #Class, #Health, #Level, #Agility, #Strength, #Stamina, #Armor, #HoursPlayed, #PlayedSince, #AvgPlayTime, #TotalPoints)";
// Sends out Command
SqlCommand comm = new SqlCommand();
comm.CommandText = strSQL; //Commander knows what to say
comm.Connection = Conn; //Where's the phone? Here it is
// Adds all parameters.
comm.Parameters.AddWithValue("#CharName", CharName);
comm.Parameters.AddWithValue("#Class", Cass);
comm.Parameters.AddWithValue("#Health", Health);
comm.Parameters.AddWithValue("#Level", Lvl);
comm.Parameters.AddWithValue("#Agility", Agility);
comm.Parameters.AddWithValue("#Strength", Strength);
comm.Parameters.AddWithValue("#Stamina", Stamina);
comm.Parameters.AddWithValue("#Armor", Armor);
comm.Parameters.AddWithValue("#HoursPlayed", HoursPlayed);
comm.Parameters.AddWithValue("#PlayedSince", PlayedSince);
comm.Parameters.AddWithValue("#AvgPlayTime", AvgPlayTime);
comm.Parameters.AddWithValue("#TotalPoints", TotalPoints);
//Attempts to connect to the Database server
try
{
//Calls to the database server, like dialing a phone.
Conn.Open();
int intRecs = comm.ExecuteNonQuery();
//Output to rhe user the success of inserting records
strResult = $"SUCCESS: Inserted {intRecs} records.";
//Close the Connection to Database Server
Conn.Close();
}
//This will catch any errors if we run into issues connecting.
catch (Exception err)
{
//Displays The Error.
strResult = "ERROR: " + err.Message;
}
finally
{
}
return strResult;
}
public DataSet SearchPerson(String strCharName, String strCharID)
{
//Create a dataset to return filled
DataSet ds = new DataSet();
//Create a command for our SQL statement
SqlCommand comm = new SqlCommand();
//Write a Select Statement to perform Search
String strSQL = "SELECT CharID, CharName, Class, Health, Level, Agility, Strength, Armor, HoursPlayed, PlayedSince, AvgPlayTime, TotalPoints FROM Chars WHERE 0=0";
//If the First/Last Name is filled in include it as search criteria
if (strCharName.Length > 0)
{
strSQL += " AND CharName LIKE #CharName";
comm.Parameters.AddWithValue("#CharName", "%" + strCharName + "%");
}
if (strCharID.Length > 0)
{
strSQL += " AND CharID LIKE #CharID";
comm.Parameters.AddWithValue("#CharID", "%" + strCharID + "%");
}
//Create DB tools and Configure
//*********************************************************************************************
SqlConnection conn = new SqlConnection();
//Create the who, what where of the DB
string strConn = #GetConnected();
conn.ConnectionString = strConn;
//Fill in basic info to command object
comm.Connection = conn; //tell the commander what connection to use
comm.CommandText = strSQL; //tell the command what to say
//Create Data Adapter
SqlDataAdapter da = new SqlDataAdapter();
da.SelectCommand = comm; //commander needs a translator(dataAdapter) to speak with datasets
//*********************************************************************************************
//Get Data
conn.Open(); //Open the connection (pick up the phone)
da.Fill(ds, "Chars_Temp"); //Fill the dataset with results from database and call it "EBooks_Temp"
conn.Close(); //Close the connection (hangs up phone)
//Return the data
return ds;
}
private string GetConnected()
{
return ************************
}
// Initialize Public Class from person().
public Character()
{
charname = "";
cass = "";
health = 25;
lvl = 1;
agility = 1;
strength = 1;
stamina = 1;
armor = 10;
hoursplayed = 0;
playedsince = DateTime.Now;
feedback = "";
}
}
}
Validation:
//Allen J. Gawlowicz
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Lab5_validation
{
public class ValidationLibrary
{
bool result = false;
string temp;
//Validates for alphabetical input only.
public static bool Goodchar(string temp)
{
bool result = false;
string strGoodChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
// FOR EACH charcacter in Variable, check to make sure it is Alphabetical
foreach (char ch in strGoodChars.ToUpper())
{//If the variable contains only alphabetical, result= true.
if (temp.Contains(ch))
{
result = true;
}
}
return result;
}
//Checks to see if user inputs a bad word.
public static bool GotBadWords(string temp)
{
bool result = false;
//Array which holds all bad words to ever exist!!
string[] strBadwords = { "POOP", "HOMEWORK", "CACA" };
// FOR EACH word in the array it checks to see if input value matches.
foreach (string strBW in strBadwords)
{
if (temp.Contains(strBW))
{
result = true;
}
}
return result;
}
//Function which checks if user inputs.
public static bool IsItFilledIn(string temp)
{
bool result = false;
//If the number of characters entered is greater than 0 (then obviously), it is filled in (true).
if (temp.Length > 0)
{
result = true;
}
return result;
}
//Function which checks if user input is a future date.
public static bool IsAFutureDate(DateTime temp)
{
bool blnResult;
//IF usrr input is today or before, then blnRes = false.
if (temp <= DateTime.Now)
{
blnResult = false;
}
else
{
blnResult = true;
}
return blnResult;
}
//Function which checks user email input for '#' and '.'.
public static bool IsValidEmail(string temp)
{
bool blnResult = true;
int atLocation = temp.IndexOf("#");
int NexttatLocation = temp.IndexOf("#", atLocation + 1);
int periodLocation = temp.LastIndexOf(".");
if (temp.Length < 8)
{
blnResult = false;
}
else if (atLocation < 2)
{
}
else if (periodLocation + 2 > (temp.Length))
{
blnResult = false;
}
return blnResult;
}
//Function which checks for '.' in the input URL.
public static bool IsValidUrl(string temp)
{
bool blnResult = true;
int NexttatLocation = temp.IndexOf(".");
int periodLocation = temp.LastIndexOf(".");
if (temp.Length < 10)
{
blnResult = false;
}
else if (periodLocation + 2 > (temp.Length))
{
blnResult = false;
}
return blnResult;
}
//Function which checks user input for a specific length. ('min' will changed depending on variable in main program).
public static bool IsMinimumAmount(string temp, int min)
{
bool blnResult;
if (temp.Length >= min)
{
blnResult = true;
}
else
{
blnResult = false;
}
return blnResult;
}
public static bool IsMinimumAmount(double temp, double min)
{
bool blnResult;
if (temp >= min)
{
blnResult = true;
}
else
{
blnResult = false;
}
return blnResult;
}
}
}
Program.cs:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Forms;
using Lab5_validation;
namespace FinalAJG
{
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
}
}
}
Your Parse methods cannot accept an empty string. Instead, I suggest TryParse, which will try to parse the string, and if it fails then you will get the default value for the assignment target. So if you do this:
int.TryParse(txtHealth.Text, out temp.Health);
The value of temp.Health will be the parsed value of the string, or it will be the default (which is 0 for int).
The same approach can be used for double and DateTime, so:
double.TryParse(txtHoursPlayed.Text, out temp.HoursPlayed);
DateTime.TryParse(txtPlayedSince.Text, out temp.PlayedSince);
Alternatively, you can check if the input string IsNullOrWhiteSpace and assign a default if that's true:
temp.Health = string.IsNullOrWhiteSpace(txtHealth.Text) ? 0 : int.Parse(txtHealth.Text);
But of course the TryParse methods are generally cleaner.
Hi im trying to make a notification system. Basically when i add a new row, a notification will be shown on my notificationspage realtime (i use signalR with razor pages asp.net). But for some reason when i get on that page, i get these errors: Unable to cast object of type 'System.DBNull' to type 'System.DateTime'.
at myWebApp.Controllers.SpeedListener.GetAlarmList() in \myWebApp\Controllers\SpeedListener.cs:line 83
at myWebApp.Controllers.SpeedListener.ListenForAlarmNotifications() in \myWebApp\Controllers\SpeedListener.cs:line 43
So apparently theres a problem at the controller.
Here is the code of the controller
namespace myWebApp.Controllers
{
public class SpeedListener :Controller
{
private IHubContext<speedalarmhub> _hubContext;
private IMemoryCache _cache;
public SpeedListener(IHubContext<speedalarmhub> hubContext,IMemoryCache cache)
{
_hubContext = hubContext;
_cache = cache;
}
public static string cs = Database.Database.Connector();
public void ListenForAlarmNotifications()
{
NpgsqlConnection conn = new NpgsqlConnection(cs);
conn.StateChange += conn_StateChange;
conn.Open();
var listenCommand = conn.CreateCommand();
listenCommand.CommandText = $"listen notifytickets;";
listenCommand.ExecuteNonQuery();
conn.Notification += PostgresNotificationReceived;
_hubContext.Clients.All.SendAsync(this.GetAlarmList());
while (true)
{
conn.Wait();
}
}
private void PostgresNotificationReceived(object sender, NpgsqlNotificationEventArgs e)
{
string actionName = e.Payload.ToString();
string actionType = "";
if (actionName.Contains("DELETE"))
{
actionType = "Delete";
}
if (actionName.Contains("UPDATE"))
{
actionType = "Update";
}
if (actionName.Contains("INSERT"))
{
actionType = "Insert";
}
_hubContext.Clients.All.SendAsync("ReceiveMessage", this.GetAlarmList());
}
public string GetAlarmList()
{
List<NotificationModel> not = new List<NotificationModel>();
using var con = new NpgsqlConnection(cs);
{
string query = "Select datumnu, bericht FROM notification";
using NpgsqlCommand cmd = new NpgsqlCommand(query, con);
{
cmd.Connection = con;
con.Open();
using (NpgsqlDataReader dr = cmd.ExecuteReader())
{
while (dr.Read())
{
not.Add(new NotificationModel { Datenow = ((DateTime) dr["datumnu"]).ToString("yyyy/MM/dd"), Bericht = dr["bericht"].ToString() });
}
}
con.Close();
}
}
_cache.Set("notification", SerializeObjectToJson(not));
return _cache.Get("notification").ToString();
}
public String SerializeObjectToJson(Object notification)
{
try
{
return Newtonsoft.Json.JsonConvert.SerializeObject(notification);
}
catch (Exception) { return null; }
}
private void conn_StateChange(object sender, System.Data.StateChangeEventArgs e)
{
_hubContext.Clients.All.SendAsync("Current State: " + e.CurrentState.ToString() + " Original State: " + e.OriginalState.ToString(), "connection state changed");
}
}
}
If needed here is my hub
namespace myWebApp.Hubs
{
public class speedalarmhub : Hub
{
private IMemoryCache _cache;
private IHubContext<speedalarmhub> _hubContext;
public speedalarmhub(IMemoryCache cache, IHubContext<speedalarmhub> hubContext)
{
_cache = cache;
_hubContext = hubContext;
}
public async Task SendMessage()
{
if (!_cache.TryGetValue("notification", out string response))
{
SpeedListener speedlist = new SpeedListener(_hubContext,_cache);
speedlist.ListenForAlarmNotifications();
string jsonspeedalarm = speedlist.GetAlarmList();
_cache.Set("notification", jsonspeedalarm);
await Clients.All.SendAsync("ReceiveMessage", _cache.Get("notification").ToString());
}
else
{
await Clients.All.SendAsync("ReceiveMessage", _cache.Get("notification").ToString());
}
}
}
}
the table name in postgresql is called 'notification', i have two column called 'bericht' with type varchar and 'datumnu' with type date.
Edit suggested by mjwills
DateTime don't accept null value. Check if the value is null and assigne a default value
while (dr.Read())
{
not.Add(new NotificationModel { Datenow = ((DateTime) dr["datumnu"]).ToString("yyyy/MM/dd"), Bericht = dr["bericht"].ToString() });
}
Become
while (dr.Read())
{
DateTime defaultDateTime = DateTime.Now;
if(dr.IsNull("datumnu")){
defaultDateTime = (DateTime)dr["datumnu"];
}
not.Add(new NotificationModel { Datenow = defaultDateTime, Bericht = dr["bericht"].ToString() });
}
in single line
while (dr.Read())
{
not.Add(new NotificationModel { Datenow = (dr.IsNull("datumnu") ? DateTime.Now : (DateTime)dr["datumnu"]), Bericht = dr["bericht"].ToString() });
}
i have design small repository pattern for ado.net. now i could not manage to handle exception proper way. i want to push error to calling environment if any occur. if no error occur then result set will be push to calling environment.
i have repository called AdoRepository which extend other repository classes like employee etc. we are calling employee repository function from mvc controller. so i want to push error to mvc controller from employee repository if any occur during data fetch, if no error occur then data will be sent to mvc controller. here is my full code. please have look and share the idea for best design. if possible paste rectified code here.
Base repository
public abstract class AdoRepository<T> where T : class
{
private SqlConnection _connection;
public virtual void Status(bool IsError, string strErrMsg)
{
}
public AdoRepository(string connectionString)
{
_connection = new SqlConnection(connectionString);
}
public virtual T PopulateRecord(SqlDataReader reader)
{
return null;
}
public virtual void GetDataCount(int count)
{
}
protected IEnumerable<T> GetRecords(SqlCommand command)
{
var reader = (SqlDataReader) null;
var list = new List<T>();
try
{
command.Connection = _connection;
_connection.Open();
reader = command.ExecuteReader();
while (reader.Read())
{
list.Add(PopulateRecord(reader));
}
reader.NextResult();
if (reader.HasRows)
{
while (reader.Read())
{
GetDataCount(Convert.ToInt32(reader["Count"].ToString()));
}
}
Status(false, "");
}
catch (Exception ex)
{
Status(true, ex.Message);
}
finally
{
// Always call Close when done reading.
reader.Close();
_connection.Close();
_connection.Dispose();
}
return list;
}
protected T GetRecord(SqlCommand command)
{
var reader = (SqlDataReader)null;
T record = null;
try
{
command.Connection = _connection;
_connection.Open();
reader = command.ExecuteReader();
while (reader.Read())
{
record = PopulateRecord(reader);
Status(false, "");
break;
}
}
catch (Exception ex)
{
Status(true, ex.Message);
}
finally
{
reader.Close();
_connection.Close();
_connection.Dispose();
}
return record;
}
protected IEnumerable<T> ExecuteStoredProc(SqlCommand command)
{
var reader = (SqlDataReader)null;
var list = new List<T>();
try
{
command.Connection = _connection;
command.CommandType = CommandType.StoredProcedure;
_connection.Open();
reader = command.ExecuteReader();
while (reader.Read())
{
var record = PopulateRecord(reader);
if (record != null) list.Add(record);
}
}
finally
{
// Always call Close when done reading.
reader.Close();
_connection.Close();
_connection.Dispose();
}
return list;
}
}
StudentRepository which extend base AdoRepository
-----------------------------------------------
public class StudentRepository : AdoRepository<Student>
{
public int DataCounter { get; set; }
public bool hasError { get; set; }
public string ErrorMessage { get; set; }
public StudentRepository(string connectionString)
: base(connectionString)
{
}
public IEnumerable<Student> GetAll()
{
// DBAs across the country are having strokes
// over this next command!
using (var command = new SqlCommand("SELECT ID, FirstName,LastName,IsActive,StateName,CityName FROM vwListStudents"))
{
return GetRecords(command);
}
}
public Student GetById(string id)
{
// PARAMETERIZED QUERIES!
using (var command = new SqlCommand("SELECT ID, FirstName,LastName,IsActive,StateName,CityName FROM vwListStudents WHERE Id = #id"))
{
command.Parameters.Add(new ObjectParameter("id", id));
return GetRecord(command);
}
}
public IEnumerable<Student> GetStudents(int StartIndex, int EndIndex, string sortCol, string sortOrder)
{
string strSQL = "SELECT * FROM vwListStudents WHERE ID >=" + StartIndex + " AND ID <=" + EndIndex;
strSQL += " ORDER BY " + sortCol + " " + sortOrder;
strSQL += ";SELECT COUNT(*) AS Count FROM vwListStudents";
var command = new SqlCommand(strSQL);
return GetRecords(command);
}
public override Student PopulateRecord(SqlDataReader reader)
{
return new Student
{
ID = Convert.ToInt32(reader["ID"].ToString()),
FirstName = reader["FirstName"].ToString(),
LastName = reader["LastName"].ToString(),
IsActive = Convert.ToBoolean(reader["IsActive"]),
StateID = Convert.ToInt32(reader["StateID"].ToString()),
StateName = reader["StateName"].ToString(),
CityID = Convert.ToInt32(reader["CityID"].ToString()),
CityName = reader["CityName"].ToString()
};
}
public override void GetDataCount(int count)
{
DataCounter = count;
}
public override void Status(bool IsError, string strErrMsg)
{
hasError = IsError;
ErrorMessage = strErrMsg;
}
}
calling StudentRepository from mvc controller like below way
public class StudentController : Controller
{
private StudentRepository _data;
public StudentController()
{
_data = new StudentRepository(System.Configuration.ConfigurationManager.ConnectionStrings["StudentDBContext"].ConnectionString);
}
// GET: Stuent
public ActionResult List(StudentListViewModel oSVm)
{
StudentListViewModel SVm = new StudentListViewModel();
SVm.SetUpParams(oSVm);
SVm.Students = _data.GetStudents(SVm.StartIndex, SVm.EndIndex, SVm.sort, oSVm.sortdir).ToList();
SVm.RowCount = _data.DataCounter;
return View("ListStudents",SVm);
}
}
I don't get the point of this:
catch (Exception ex)
{
Status(true, ex.Message);
}
Simply not catch the exception and let it bubble up to the caller who, according to you, will know to handle it. No callbacks necessary.
Storing retrieved data in instance state seems like a bad way to go. Rather, return an object with that data. That results in a more straight forward API and has less mutable state.
finally
{
reader.Close();
_connection.Close();
_connection.Dispose();
}
There is a better way to go about this: Wrap resources in a using statement. In particular, part ways with the superstitious double dispose pattern.
Let the caller deal with the exception making sure that you log a decent error message (showing all relevant fields). The Status class will annoy the hell out of support people as it swallows the stack trace and says nothing about the data which has caused the error. DB exceptions are often caused by malformed data so its important to have this data logged when things go wrong.
As an aside, your PopulateRecord and GetDataCount methods should be abstract as the base versions do nothing. Another dev could easily think they don't need to implement these methods and would be left with a class with useless PopulateRecord and GetDataCount methods.
I have a code a base that iterates through a list of records and does the follwing
'select * from Table to see if fields exist
then later in the interation
'select * from Table to retreive some data
then farther down in the interation
select field1, field2 from Table to get certain pieces of information
I need to do all of these functions for each record. Would the process speed up if I called the query once for each record and hold the data in a datatable and retreive what I need from there? Or is there another more efficient way to not have to make 3 db calls to the same table which will speed up the process?
You can cache query data to System.Data.DataTable. To simplify things I has written CMyDynaset class, which populates DataTable with data from DB. Here how to use it for example with MySQL:
using System;
using System.Data.Common;
using MySql.Data.MySqlClient;
namesapce MyProg
{
class Program
{
private const string strMyConnection = "Host=localhost;Database=mydb;User Id=myuser;Password=mypsw";
static void Main(string[] args)
{
using (MySqlConnection myConnection = new MySqlConnection(strMyConnection))
{
using (MyDb.CMyDynaset dyn = new MyDb.CMyDynaset(myConnection, MySqlClientFactory.Instance))
{
// populate dyn.Table (System.Data.DataTable)
dyn.Init("select * from Table");
dyn.Load();
// access fields
foreach (DataColumn column in dyn.Table.Columns)
{
// ...
}
// get data
long nCountAll = dyn.Table.Rows.Count; // rows count
foreach (DataRow row in dyn.Table.Rows)
{
Object val1 = row[1]; // acess by index
Object val2 = row["id"]; // acess by name
// ...
}
// update data
dyn.Table.Rows[0]["name"] = "ABC";
dyn.Update();
}
}
}
}
}
CMyDynaset class (CMyDynaset.cs):
// CMyDynaset.cs
using System;
using System.Data.Common;
namespace MyDb
{
/// <summary>
/// Summary description for CMyDynaset.
/// </summary>
public class CMyDynaset : IDisposable
{
public System.Data.DataTable Table = null;
// private
private DbConnection myConnection = null;
private DbProviderFactory myFactory = null;
private DbDataAdapter dataAdap = null;
private DbCommandBuilder cmdBld = null;
private bool bIsSchema = false;
public CMyDynaset(DbConnection conn, DbProviderFactory factory)
{
this.myConnection = conn;
this.myFactory = factory;
}
#region IDisposable Members
public void Dispose()
{
if (this.Table != null)
{
this.Table.Dispose();
this.Table = null;
}
if (this.cmdBld != null)
{
this.cmdBld.Dispose();
this.cmdBld = null;
}
if (this.dataAdap != null)
{
this.dataAdap.Dispose();
this.dataAdap = null;
}
// This object will be cleaned up by the Dispose method.
// Therefore, you should call GC.SupressFinalize to
// take this object off the finalization queue
// and prevent finalization code for this object
// from executing a second time.
GC.SuppressFinalize(this);
}
#endregion
// Init
public void Init(string strSelect)
{
DbCommand cmdSel = this.myConnection.CreateCommand();
cmdSel.CommandText = strSelect;
this.dataAdap = this.myFactory.CreateDataAdapter();
this.dataAdap.SelectCommand = cmdSel;
this.cmdBld = this.myFactory.CreateCommandBuilder();
this.cmdBld.DataAdapter = this.dataAdap;
this.Table = new System.Data.DataTable();
// schema
this.bIsSchema = false;
}
public void AddParameter(string name, object value)
{
DbParameter param = this.dataAdap.SelectCommand.CreateParameter();
param.ParameterName = name;
param.Value = value;
this.dataAdap.SelectCommand.Parameters.Add(param);
}
public void AddParameter(DbParameter param)
{
this.dataAdap.SelectCommand.Parameters.Add(param);
}
// Open, Close
private void Open(ref bool bClose)
{
if (this.myConnection.State == System.Data.ConnectionState.Closed)
{
this.myConnection.Open();
bClose = true;
}
if (!this.bIsSchema)
{ // schema
this.dataAdap.FillSchema(this.Table, System.Data.SchemaType.Mapped);
this.bIsSchema = true;
}
}
private void Close(bool bClose)
{
if (bClose)
this.myConnection.Close();
}
// Load, Update
public void Load()
{
bool bClose = false;
try
{
this.Table.Clear();
this.Open(ref bClose);
this.dataAdap.Fill(this.Table);
}
catch (System.Exception ex)
{
throw ex;
}
finally
{
this.Close(bClose);
}
}
public void Update()
{
bool bClose = false;
try
{
this.Open(ref bClose);
this.dataAdap.Update(this.Table);
}
catch (System.Exception ex)
{
throw ex;
}
finally
{
this.Close(bClose);
}
}
}
}
I'm having class Report and class Program, what I want to accomplish (no luck so far) is to send data from class Program and method SaveRep() to class Report method Save() and save it in this method.
I apologize if the question is badly formulated, I am really stuck at this, please help. Thanks
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Configuration;
using System.Data.SqlClient;
namespace Application
{
class Program
{
static void Main(string[] args)
{
//call method SaveRep
}
public void SaveRep(...)
{
int RepID = 1;
string Data1 = "SomeData1"
string Data2 = "SomeData2"
//This method should send the data above to method Save() in Report class
//data to this method will be provided from another method.
}
}
public class Report
{
private static int _repID;
public int RepID
{
get { return _repID; }
set { _repID = value; }
}
private static string _data1;
public string Data1
{
get { return _data1; }
set { _data1 = value; }
}
private static string __data2;
public string Data1
{
get { return _data2; }
set { _data2 = value; }
}
public void Save()
{
string strConnectionString = (#"server=(local)\sqlexpress;Integrated Security=True;database=DataBase");
SqlConnection connection = new SqlConnection(strConnectionString);
connection.Open();
// This method should save all data (RepID,Data1,Data2)
// to the DB, provided to her by SaveRep method from Program class.
// Properties are columns from a database
}
}
}
public void Save()
{
string yourConnString="Replace with Your Database ConnectionString";
using(SqlConnection connection = new SqlConnection(yourConnString))
{
string sqlStatement = "INSERT Table1(Data1) VALUES(#Data1)";
using(SqlCommand cmd = new SqlCommand(sqlStatement, connection))
{
cmd.CommandType = CommandType.Text;
cmd.Parameters.AddWithValue("#Data1",Data1);
//add more parameters as needed and update insert query
try
{
connection.Open();
cmd.ExecuteNonQuery();
}
catch(Exception ex)
{
//log error
}
}
}
}
Assuming your Data1 is the name of your Column name. Update the sqlStatement to have your relevant columns.
Now in SaveRep, you can call it like
public void SaveRep()
{
Report objReport=new Report();
objReport.Data1="Something somethinng";
objReport.Save();
}
string connectionString = ....; //adjust your connection string
string queryString = "INSERT INTO ....;"; //Adjust your query
using (SqlConnection connection = new SqlConnection(
connectionString))
{
using( SqlCommand command = new SqlCommand(
queryString, connection))
{
connection.Open();
command .ExecuteNonQuery();
}
}
One tiny thing that the previous speakers have not noticed, is that your Report class is severely broken. Why did you use static keywords on the variables? Do not do that! Well, unless you know what it does, and here I have the feeling you don't. Update your Report class to look like this:
public class Report
{
public int RepID {get;set;}
public string Data1 {get;set;}
public string Data2 {get;set;}
public void Save()
{
// what others said
}
}
'static' in your original code makes all te variables global/shared. That means that if you'd create 500 Report objects with new, they all would have the same one ID, Data1 and Data2. Any change to any single of those 500 objects would cause all the objects to change. But it's be misleading, the objects would not change. They would simply have the same data. Looking at the "ID" field, I think you'd rather want a separate objects with separate data for separate records..