So I have this code that checks if new data is added to online database by comparing the rows of online and local database. If new data is found it inserts the new data to local database.
public class Reservation
{
public string res_no { get; set; }
public string mem_fname { get; set; }
}
My Code :
private async void updateDineList()
{
DBconnector.OpenConnection();
//Gets data from online database
HttpClient client = new HttpClient();
var response = await client.GetStringAsync("http://example.com/Reservation/view_pending_reservation");
var persons = JsonConvert.DeserializeObject<List<Reservation>>(response);
//Gets data from Local database
string string_reservation = "SELECT res_no,mem_fname FROM res_no WHERE res_status='pending';";
DataTable reservation_table = new DataTable();
MySqlDataAdapter adapter_reservartion = new MySqlDataAdapter(string_reservation, DBconnector.Connection);
adapter_reservartion.Fill(reservation_table);
//Gets the row of each table
int local = reservation_table.Rows.Count;
int online = persons.Count;
//Compares rows of online and local database
if (local < online)
{
//if the rows of online database is greater than local database
//inserts the new data from local database
string Command_membership = "INSERT INTO reservation_details (res_no,mem_fname) VALUES (#res_no, #mem_fname);";
for (int i = local; i < online; i++)
{
//inserts new data from online to local database
using (MySqlCommand myCmd = new MySqlCommand(Command_membership, DBconnector.Connection))
{
myCmd.CommandType = CommandType.Text;
myCmd.Parameters.AddWithValue("#res_no", persons[i].res_no);
myCmd.Parameters.AddWithValue("#mem_fname", persons[i].mem_fname);
myCmd.ExecuteNonQuery();
}
}
MessageBox.Show("New Records Found");
}
else
{
MessageBox.Show("No new Records");
}
DBconnector.Connection.Close();
}
So my question is there any problem could occur with this code, it works fine but is there any way to improve this. I know MySQL replication is better but I am only using free Web Hosting with few MySQL privileges.
The clear improvement is not to create a new command for every row. You should either create the command and parameters once and then set the parameters and call for each row, or better still package the set of updates into a single structure, like and xml string, and then pass the whole lot to the database via a stored procedure call.
Other probably problematic issue is that you are checking purely based on row counts. Don't know if that is valid in your scenario but it sounds dangerous. What if rows are deleted? or is that not possible in your scenario. Some other way of checking last updates would probably be preferable.
Without more context that's about all I can see.
Related
I'm completely new to C# programming and I'm trying to learn on my own. Currently I'm building a mini-project to exercise.
I understand that the user layer should not have any data query for security reasons perhaps?
So I have created a separate Data Access class to retrieve data. This is what my data access class looks like(I'll be using stored procedures for better security once I learn how to use it):
public class DataAccess
{
public List<Customer> FilteredCustomersList(string name)
{
using (IDbConnection connection = new MySql.Data.MySqlClient.MySqlConnection(Helper.CnnVal("FineCreteDB")))
{
var output = connection.Query<Customer>($"SELECT * from `Customers` WHERE `Cust_Name` LIKE '{name}'").ToList();
return output;
}
}
Basically I send over a string from the user form to query the database, the data is retrieved and stored in a list. User form:
private void RetrieveData()
{
try
{
DataAccess db = new DataAccess();
filteredcustomers = db.FilteredCustomersList(CustomerNameTxtBox_AutoComplete.Text);
ntn_num = filteredcustomers.Select(x => x.Cust_NTN).ElementAt(0);
strn_num = filteredcustomers.Select(x => x.Cust_STRN).ElementAt(0);
address = filteredcustomers.Select(x => x.Cust_Address).ElementAt(0);
phone_num = filteredcustomers.Select(x => x.Cust_Phone).ElementAt(0);
id_num = filteredcustomers.Select(x => x.Cust_ID).ElementAt(0);
}
catch (Exception)
{
MessageBox.Show("Customer not found. If customer was recently added, try updating DB.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
DataAccess db = new DataAccess();
filteredcustomers = db.AllCustomersList();
ntn_num = "";
strn_num = "";
address = "";
phone_num = "";
}
}
On the user form side, "filteredcustomers" holds the list of data sent back, now here is the problem: I use the filteredcustomers list to extract the different column values like so:
address = filteredcustomers.Select(x => x.Cust_Address).ElementAt(0);
and then use them to populate the respective textboxes like:
Address_TxtBox.Text = address;
Everything works fine, but I don't want the userform to have these queries for all individual columns, because from what I've understood so far, this is bad programming and bad for security as well.
Can anyone guide me how I can keep the values in Data Access layer and just call them into my form?
I'm sorry if this is a long post, I'm just learning and wanted to be as detailed as possible.
You're already doing everything reasonably correctly as per how Dapper is to be used. Dapper doesn't maintain a local graph of entities from the db, track changes to it and automatically save them. If you want that, use something like EF
For dapper you retrieve data with a SELECT and send it back with an UPDATE
If you're only expecting one Customer for the name, do this:
var output = connection.QueryFirstOrDefault<Customer>($"SELECT * from `Customers` WHERE `Cust_Name` LIKE #n", new { n = name });
https://dapper-tutorial.net/queryfirst
This will return just one customer instance (or null; check it!) meaning you can tidy up your form code to:
c = db.FilteredCustomer(CustomerNameTxtBox_AutoComplete.Text);
ntn_num = c?.Cust_NTN;
strn_num = c?.Cust_STRN;
And so on
Your "if customer was recently added try updating db" doesn't really make sense- the query is done live, so the db is about as up to date as it can be
I am using SQLite database for my Windows Store app. I completed my requirement of usage of Database with the help of MSFT samples & Blogs. Now I want to Change/Update the Data which is in the Database File..
My Insert code like this..
using (var db = new SQLite.SQLiteConnection(dbpath))
{
db.Insert(new ItemEpub.EpubBookList()
{
BookName = file.DisplayName,
BookPath = file.Path,
}
);
db.Commit();
db.Dispose();
db.Close();
}
I didn't get the Syntax & any simple information about Update the Database table.
Right now my code is:
StorageFile DataFile = await ApplicationData.Current.LocalFolder.GetFileAsync("Epub.db3");
using (var db = new SQLite.SQLiteConnection(dbpath))
{
db.Update EpubBookList Set
string sql = UPDATE EpubBookList SET LastVisitedPage = '" + lastVisistedPageIndex + "',";
db.Dispose();
db.Close();
}
May be this code looks like ugly, Apart from this code give me any Suggestion on Update the Exiting row in metro apps
To update the existing record, first you need to get that object via SQL query [Query<T>(...)] or Get<T>(...) method. Then update the object properties and call Update(...) method.
using (var db = new SQLite.SQLiteConnection(dbpath))
{
var objPerson = db.Query<Person>("select * from dbPerson where PersonId = ?", 9);
/*** or ***/
var objPerson = db.Get<Person>(9); // Here 9 is primary key value
/*** update the object ***/
objPerson.FirstName = "Matt";
db.Update(objPerson);
}
You don't need to call db.Dispose(); & db.Close(); because you are using using.
I have this small problem with the DriveInfo Class.
I know that the error is specific to the "IsReady" Property but I just don't know how to define it..
namespace Csp.Test.ConsoleApp
{
public class Program
{
public static void Main()
{
//Create the server object - You will need create a list of the server objects.
Server server = new Server();
//Get all drives information
List<DriveInfo> driveList = DriveInfo.GetDrives().ToList<DriveInfo>();
//Insert information of one server - You will need get information of all servers
server.ServerID = 0; //Here is necessery put PK key. I recommend doing the SQL server will automatically generate the PK.
server.ServerName = string.Concat("Server ", driveList.Count);
//Inserts information in the newServers object
for (int i = 0; i < driveList.Count; i++)
{
ServerDrive serverDrives = new ServerDrive();
//Put here all the information to obeject Server
serverDrives.DriveLabel = driveList[i].Name;
serverDrives.TotalSpace = driveList[i].TotalSize;
serverDrives.DriveLetter = driveList[i].VolumeLabel;
serverDrives.FreeSpace = driveList[i].TotalFreeSpace;
// server.ListServerDrives.Add(serverDrives);
server.ServerDrives.Add(serverDrives);
}
//Add the information to an SQL Database using Linq.
DataClasses1DataContext db = new DataClasses1DataContext(#"sqlserver");
// db.Servers.InsertAllOnSubmit(server);
db.Servers.InsertOnSubmit(server);
db.SubmitChanges();
}
Any help would be greatly appreciated.
Change the following line:
List<DriveInfo> driveList = DriveInfo.GetDrives().Where(x=>x.IsReady).ToList();
Note that you can still get an IOException if the drive state changes between getting the drives list and querying the DriveInfo, so it is best you use a try-catch when accessing the DriveInfo.
I have a service which continuously writes data in a separate thread into SQL database.Now from the same service if i am trying to read from the same table, since i already am writing into it,I get this exception : There is already an open DataReader associated with this Command which must be closed first.
So can anyone help me how to do this simultaneously?
Here s my code for reading data:
public Collection ReadData(string query)
{
{
_result = new Collection<string[]>();
string[] tempResult;
SqlDataReader _readerRead;
using (_command = new SqlCommand(query, _readConnection))
{
_readerRead = _command.ExecuteReader();
while (_readerRead.Read())
{
tempResult = new string[4];
tempResult[0] = _reader[0].ToString();
tempResult[1] = _reader[1].ToString();
tempResult[2] = _reader[2].ToString();
tempResult[3] = _reader[3].ToString();
_result.Add(tempResult);
//Console.WriteLine("Name : {0} Type : {1} Value : {2} timestamp : {3}", _reader[0], _reader[1], _reader[2], _reader[3]);
}
if (_readerRead != null)
{
_readerRead.Close();
}
_readConnection.Close();
return _result;
}
}
}
and here it is for writing to it :
public void WriteData(Collection<TagInfo> tagInfoList)
{
int i = 0;
for (i = 0; i < tagInfoList.Count; i++)
{
using( _command = new SqlCommand(insert statement here)
{
_command.Parameters.AddWithValue("Name", tagInfoList[i].Name);
_command.Parameters.AddWithValue("Type", tagInfoList[i].TagType);
_command.Parameters.AddWithValue("Value", tagInfoList[i].Value);
_reader = _command.ExecuteReader();
if (_reader != null)
{
_reader.Close();
}
}
}
}
You need a different SQLConnection to the database for your writer. You cannot use the same db connection for both.
Although its possible to do, using a separate connection I would question why you need to do this.
If you are reading and writing data to one table in the same service you will be placing unnecessary load on one SQL table, and depending on the number of queries you intend to make this could cause you problems. If you already have this data (in a different thread) why not Marshall the data from the background thread to where you need it as you write it into the database, and you don't need to read the data anymore.
However.... it is difficult to give an fair answer without seeing the code/what you are looking to achieve.
I created a program a while ago using C# that does some automation for a completely different program, but found that I need to access data from a Lotus Notes database. The only problem is, I can only seem to figure out how to open the database by the server's name (using session.GetDatabase())... I can't figure out how to open it by Replica ID. Does anyone know how I would go about that? (I don't want my program going down every time the server changes.)
public static string[] GetLotusNotesHelpTickets()
{
NotesSession session = new NotesSession();
session.Initialize(Password);
// 85256B45:000EE057 = NTNOTES1A Server Replica ID
NotesDatabase database = session.GetDatabase("NTNOTES1A", "is/gs/gshd.nsf", false);
string SearchFormula = string.Concat("Form = \"Call Ticket\""
, " & GroupAssignedTo = \"Business Systems\""
, " & CallStatus = \"Open\"");
NotesDocumentCollection collection = database.Search(SearchFormula, null, 0);
NotesDocument document = collection.GetFirstDocument();
string[] ticketList = new string[collection.Count];
for (int i = 0; i < collection.Count; ++i)
{
ticketList[i] = ((object[])(document.GetItemValue("TicketNumber")))[0].ToString();
document = collection.GetNextDocument(document);
}
document = null;
collection = null;
database = null;
session = null;
return ticketList;
}
This code is working fine, but if the server changed from NTNOTES1A, then nothing is going to work anymore.
you'll need to use the notesDbDirectory.OpenDatabaseByReplicaID(rid$) method. To get the NotesDbDirectory, you can use the getDbDirectory method of the session
Set notesDbDirectory = notesSession.GetDbDirectory( serverName$ )
So you can use the code below to get a database by replicaID.
public static string[] GetLotusNotesHelpTickets()
{
NotesSession session = new NotesSession();
session.Initialize(Password);
Set notesDBDirectory = session.GetDbDirectory("NTNOTES1A")
// 85256B45:000EE057 = NTNOTES1A Server Replica ID
NotesDatabase database = notesDBDirectory.OpenDatabaseByReplicaID("85256B45:000EE057")
string SearchFormula = string.Concat("Form = \"Call Ticket\""
, " & GroupAssignedTo = \"Business Systems\""
, " & CallStatus = \"Open\"");
NotesDocumentCollection collection = database.Search(SearchFormula, null, 0);
NotesDocument document = collection.GetFirstDocument();
string[] ticketList = new string[collection.Count];
for (int i = 0; i < collection.Count; ++i)
{
ticketList[i] = ((object[])(document.GetItemValue("TicketNumber")))[0].ToString();
document = collection.GetNextDocument(document);
}
document = null;
collection = null;
database = null;
session = null;
return ticketList;
}
Unfortunately, this only solves half of your problem. I know you'd rather just tell Notes to fetch the database with a particular replicaID from the server closest to the client, just like the Notes Client does when you click on a DBLink or Bookmark. However, there is (or appears to be) no way to do that using the Notes APIs.
My suggestion is to either loop through a hard-coded list of potential servers by name, and check to see if the database is found (the OpenDatabaseByReplicaID method returns ERR_SYS_FILE_NOT_FOUND (error 0FA3) if the database is not found). If that's not a good option, perhaps you can easily expose the servername in an admin menu of your app so it can be changed easily if the server name changes at some point.
set database = new NotesDatabase("")
call database.OpenByReplicaID("repid")