What is the best approach while polling database? - c#

Currently I am doing an assignment in which I need to visit database of n number of servers to fetch some results.I have achieved this by iterating through the list of servers and raising tasks each one for each server in the collection. The task calls a function which basically makes an connection with the database,run query and disconnect from database.
My question is I am doing right by making a new connection on each polling with the database and closing it everytime or is this would be the best approach to keep a db connection open and fetch the result and then keep it open on next polling iteration.
PollingServerTimer() is being called by timer everytime.My polling timer is 3 sec.
Something like this :
private void PollingServerTimer(object sender, ElapsedEventArgs e)
{
foreach (var item in ServerOperationCollHandler)
{
if (item.RebootStatus != true)
{
PushItemIntoQueue(item);
}
}
}
public void PollingServerQueue()
{
while (isRunning)
{
this.waitHandle.WaitOne();
lock (syncRoot)
{
if (ServerQueue.Count > 0)
{
ServerOperationDataModel obj;
try
{
ServerQueue.TryDequeue(out obj);
Task GetCountFromDbTask = new Task(() => GetCountFromDb(obj));
GetCountFromDbTask.Start();
this.waitHandle.Reset();
}
catch (Exception ex)
{
MessageBox.Show("Problem encountered while finding iterim and recovery count");
isRunning = false;
break;
}
}
}
}
}
public void GetCountFromDb(ServerOperationDataModel obj)
{
ServerOperationDataModel serverObject = (ServerOperationDataModel)obj;
DataBaseHandler dbHandler = new DataBaseHandler(serverObject.DataBaseIP, serverObject.DataBasePort, serverObject.DataBaseName, serverObject.DataUserName, serverObject.DataUserPassword);
int attempts = 0;
do
{
try
{
dbHandler.connect();
}
catch (Exception ex)
{
break;
serverObject.DataBaseConnectionStatus = false;
log.Error("Connection attempt " + attempts + " failed.Retrying connection. Exception details :" + ex.ToString());
attempts++;
}
} while (attempts < _connectiontRetryAttempts && !dbHandler.isConnected());
if (dbHandler.isConnected())
{
/*Fetch Result and then get disconnect*/
dbHandler.disConnect();
}
else
{
//string msgLog = "Server : " + obj.ServerComponentIdentifier + " | " + obj.IPstring + "Connection cannot be established with the DB: " + obj.DataBaseIP + " | "+ obj.DataBasePort + " | " + obj.DataBaseName + " after a series of retries";
//LoggerUpdate.LogMessage(msgLog, LOGTYPE.POLLINGDATABASE, LoggerUpdate.ReturnLogDisplayObject(DateTime.Now, obj.ServerComponentIdentifier + "|" + obj.IPstring, Convert.ToInt16(LOGTYPE.POLLINGDATABASE), obj, msgLog));
}
}

I would not be concerned at all. Assuming you connect to the SQL Server (or a similar, enterprise DBMS), database connections are pooled at the client side which means that establishing the connection is costly only the first time the client connects to a particular db (formally: to a new, previously unseen connection string) and then each connection to the same database costs almost nothing.
If it hadn't been for pooling, applications servers would not be able to handle hundreds of concurrent browser connections that query the same data source. You would need much more than a connection every 3 seconds to cause any risk of depleting server or client resources.
You can read more on how pooling works
https://learn.microsoft.com/en-us/dotnet/framework/data/adonet/sql-server-connection-pooling
A side note: You should polish your code a little bit.
For example, you have
GetCountFromDb(ServerOperationDataModel obj)
but then
ServerOperationDataModel serverObject = (ServerOperationDataModel)obj;
Why would you need to cast the obj to another variable of the very same type?
In the catch clause, you have break and some code below it which looks unreachable.

Take a look at the .NET SqlDependency object. This allows you to register a query with a database and, using an OnChange handler, receive notification whenever the result of the query changes.

Related

Nested for loop resets after incrementing normally

Hi I'm trying to create a simple program that pings everything on our network over a minute and changes the UI based on the results of that ping.
I have a background worker that sets the variables and pings the appropriate IP address.
This is contained within a while loop that waits for ping to be true. Within this loop is a For Loop that takes in user input to ping that specific amount of times.
The for loop worked fine, and in debugging I can see i incrementing as usual.
I have a break point on the for loop line itself as well as the curly brace after it. If the user input is 2, it will run through twice, then the i variable explicitly changes to 0 and then reaches the if() statement
This was all working perfectly until I added some code within the nested for loops. But I cannot see anything that would be changing i
Here are some pictures showing what I mean
The loop has ran twice, and as such i is equal to 2. I then continue once more to the curly brace and i is now 0 and so doesn't break the loops.
Here's the entire method:
...
private async void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
// variables
bool ping = false;
int i;
// Create ping objects
// TODO read these variables in from the config screen and create a class to handle it (populatePing)
// Ping 2
ping0.SetFqdn("##");
ping0.SetIpAddress("##");
ping0.SetFriendlyName("##");
// Ping 1
ping1.SetFqdn("##");
ping1.SetIpAddress("##");
ping1.SetFriendlyName("##");
// Create file (needs updating to utilise wildcards)
using StreamWriter file = new("C:/Users/##/AppData/Roaming/log.txt", append: true);
try
{
// Create background worker
BackgroundWorker worker = (BackgroundWorker)sender;
while (!ping)
{
// Write to log file
await file.WriteAsync("Starting job...\n");
await file.WriteAsync("Requested amount of pings: " + count + "\n");
// Create date object for logs
DateTime localDate = DateTime.Now;
for (i = 0; i < count; i++)
{
// if count has reached the user input limit, break the loop
if (i == count)
{
ping = true;
}
// Create ping objects
System.Net.NetworkInformation.Ping pinger = new System.Net.NetworkInformation.Ping();
PingReply ip0_ping, ip1_ping;
try
{
if(!ping0.Equals(null))
{
try
{
// Send ping to specified IP address
ip0_ping = pinger.Send(ping0.GetIpAddress());
// Write log file
await file.WriteLineAsync(localDate.TimeOfDay + " | Friendly Name" + ping0.GetFriendlyName() + ": Ping: " + ip0_ping.Address + " status: " + ip0_ping.Status + ". time: " + ip0_ping.RoundtripTime + "ms");
// Successful ping has been sent
if(ip0_ping.Status.ToString().Contains("Success"))
{
ping0.SetSuccessfulPings(1);
} else // Unsuccessful ping has been sent
{
ping0.SetFailedPings(1);
}
}
catch(Exception d)
{
Debug.WriteLine(d.ToString());
}
} else
{
Debug.WriteLine("ERROR: ping0 is not being populated correctly");
}
if(!ping1.Equals(null))
{
try
{
// Send ping to specified IP address
ip1_ping = pinger.Send(ping1.GetIpAddress());
// Write log file
await file.WriteLineAsync(localDate.TimeOfDay + " | Friendly Name" + ping1.GetFriendlyName() + ": Ping: " + ip1_ping.Address + " status: " + ip1_ping.Status + ". time: " + ip1_ping.RoundtripTime + "ms");
// Successful ping has been sent
if (ip1_ping.Status.ToString().Contains("Success"))
{
ping1.SetSuccessfulPings(1);
}
else // Unsuccessful ping has been sent
{
ping1.SetFailedPings(1);
}
}
catch(Exception d)
{
Debug.WriteLine(d.ToString());
}
}
// wait one second
wait(1000);
}
catch (Exception b)
{
Debug.WriteLine(b.ToString());
}
}
}
} catch (Exception a)
{
Debug.WriteLine(a.ToString());
}
}
...
Sorry if this is really obvious but I'm really stumped
The method will not exit since the for loop is not entered if i == count since the condition section of your for loop evaluates if i < count.
See the following link under the for loop section: https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/statements/iteration-statements
The condition section that determines if the next iteration in the loop should be executed. If it evaluates to true or is not present, the next iteration is executed; otherwise, the loop is exited. The condition section must be a Boolean expression.
and also
The iterator section that defines what happens after each execution of the body of the loop.

How to handle an array of SQL Error codes

I have a try\catch block that handles opening a connection and inserting data into a database.
catch (SqlException ex)
{
string sqlError = null;
for (int i = 0; i < ex.Errors.Count; i++)
{
sqlError += "Error Message " + ex.Errors[i].Message + "\n" + "Stored Procedure " + ex.Errors[i].Procedure + " \n " + "Line Number " + ex.Errors[i].LineNumber;
}
LogTools.WriteLog("Sql Server error " + sqlError);
Variables.InstallStatusDetail = "Database connection failed";
if (!sqlError.Contains("connection to SQL Server"))
{
if (Variables.WriteToDatabase)
{ HostedDataBase.InsertRunStatusIntoInstallStatus('E', sqlError, null); }
}
}
I want to log sqlexceptions to the database that wont interfere with connecting and logging to the database. The problem occurs when the database cannot be found, or a login does not have the proper permissions, etc. The exception is raised, and it tries to log to the database, so when it does that, it calls the function that writes to the database and tries to access once again, but once again the same exception is raised, resulting in a loop of failed attempts to write to the database (either because the DSN cannot be found, or the user does not have proper permissions).
How can I handle sql errors that would prevent me from being able to access and write to the database, and at the same time still be able to write sql errors that would not cause this loop?
I'm slightly confused by your question but I will attempt to answer it. You have to be careful with how you handle exceptions. If you are going to attempt to reconnect to the database even though an exception was raised the first time, you may want to have some conditions checking what sort of exception was raised. This way, you know to only attempt to re-connect if it was an exception which will not be repeated over and over.
IE.
catch (SqlException ex)
{
Error = ex.ToString()
WriteToLog(Error);
CheckError();
}
Void CheckError()
{
//conditions based on error output.
}
void WriteToLog(string Error)
{
// write your error output to log
}
You should put your logging in it's own try..catch block, like this:
catch (SqlException ex)
{
string sqlError = null;
for (int i = 0; i < ex.Errors.Count; i++)
{
sqlError += "Error Message " + ex.Errors[i].Message + "\n" + "Stored Procedure " + ex.Errors[i].Procedure + " \n " + "Line Number " + ex.Errors[i].LineNumber;
}
LogTools.WriteLog("Sql Server error " + sqlError);
Variables.InstallStatusDetail = "Database connection failed";
if (!sqlError.Contains("connection to SQL Server"))
{
if (Variables.WriteToDatabase)
{
try {
//I am assuming this is where you are trying to write the error back into the database
HostedDataBase.InsertRunStatusIntoInstallStatus('E', sqlError, null);
} catch {
//ignore errors here
}
}
}
}
Unfortunately if you are writing to the same database that you don't have access to, I'm afraid you cannot do that.
I'd suggest you to use something like Log4net which can have multiple appenders (eventlog, database, file, etc). Also, log4net operates something like a fire and forget. Even if log4net has any issues logging the errors, it won't throw exceptions.

Changing Textbox.text within a method; For loop not looping due to Return

Below is a button, when pressed it calls a function that pings a bunch of IP addresses. If the IP address returns a response, it adds the IP address to the output_networkSearch.Text.
private void button_networkSearch_Click(object sender, RoutedEventArgs e)
{
output_networkSearch.Text = networkSearch(Convert.ToInt32(input_searchLimit.Text));
}
Below isn't the whole method, just the part that I can't get to work. The for loop starts at whatever the last digit on the users default gateway IP address is, and stops at whatever limit they have inputed (1 - 255).
// i is equal to the last digit in the default gateway IP, if it was 192.168.0.1 then i = 1.
for (int i = Convert.ToInt32(splitGatewayIP[3]); i <= searchLimit; i = i + 1)
{
// If the method receieves a ping reply...
if (PingHostSweep(gatewayIPRebuild + i))
{
// Returns 192.168.0. + i + ACTIVE
string response = gatewayIPRebuild + i + " ACTIVE";
return response;
}
else
{
string response = gatewayIPRebuild + i + " CLOSED";
return response;
}
}
This worked on a console application but for a WPF application it seems to run through the loop once and stop due to the return statement.
My idea to work around this would be to remove the Return Response statements and try and access the TextBox (output_networkSearch) directly.
So I would do something like:
for (int i = Convert.ToInt32(splitGatewayIP[3]); i <= searchLimit; i = i + 1)
{
// If the method receieves a ping reply...
if (PingHostSweep(gatewayIPRebuild + i))
{
// Returns 192.168.0. + i + ACTIVE
string response = gatewayIPRebuild + i + " ACTIVE";
output_networkSearch.Text = reponse;
}
else
{
string response = gatewayIPRebuild + i + " CLOSED";
output_networkSearch.Text = reponse;
}
}
HOWEVER, I can't access the textbox within the method for some reason. I've only just started learning C# so I'm not entirely familiar with how it works.
Here's an image of a partially working concept. As you can see the limit is set at 10, so it should ping IP address 1 through 10 and give an ACTIVE or CLOSED response. This did work in my console application version.
WPF version
Console version
This might do the trick for you
List<string> responses = new List<string>();
string response;
for (int i = Convert.ToInt32(splitGatewayIP[3]); i <= searchLimit; i = i + 1)
{
if (PingHostSweep(gatewayIPRebuild + i))
{
response = gatewayIPRebuild + i + " ACTIVE";
}
else
{
response = gatewayIPRebuild + i + " CLOSED";
}
responses.Add(response)
}
Now after the loop the list which is responses would have the list of all the IPs which are active and closed. Like the way you do had in the console Application.
i think you need use threading, there are need many child threading work in backend to scan, when they finish them work then response the result to MainForm, so i write some code hope can help you!
using System.Threading;
using System.Threading.Tasks;
public void Start(string ip)
{
Task.Factory.StartNew(() =>
{
// If the method receieves a ping reply...
string response;
if (PingHostSweep(ip))
{
// Returns 192.168.0. + i + ACTIVE
response = ip + " ACTIVE";
}
else
{
response = ip + " CLOSED";
}
this.Invoke((MethodInvoker)(() => { textBox1.AppendText("\r\n" + response); }));
});
}
private void button1_Click(object sender, EventArgs e)
{
for (int i = 1; i <= 255; i++)
{
Start(String.Format("192.168.100.{0}", i));
}
}
The previous answer was correct (though it didn't touch on a more advanced point that you will ultimately need to learn ... delegation and invocation ... long story ... won't bore you now).
What you wrote distills to this:
// SIDE NOTE: you cannot actually treat an IPv4 address as four "pure" quads (but that's not your question)
var notNecessarilyAHost = splitGatewayIP[3];
var searchStart = Convert.ToInt32(notNecessarilyAHost);
for (var i = searchStart; i <= searchLimit; ++i)
{
if (PingHostSweep(gatewayIPRebuild + i))
{
return $"{gatewayIPRebuild}{i} ACTIVE";
}
else
{
return $"{gatewayIPRebuild}{i} CLOSED";
}
}
...and if you (mentally) step through what you wrote it's fairly straightforward to see that the loop will only ever cycle once. Upon entry to the loop i will be equal to whatever searchStart is. Then you enter the if test. If that test is true, you fall into the true side of the branch (i.e., "...ACTIVE"). Otherwise, you'll drop into the else branch (i.e., "...CLOSED". FROM THERE...
You ALWAYS return. That will exit the loop (and the function that contains it). You will never cycle the loop again. "break" and "return" (and plausibly goto ... but that's for a different day) will ALWAYS exit the current scope (scope being a block of code wrapped by '{' and '}' (be they explicitly or implicitly written).
Following?
The previous answer was correct. It adjusts your code so that the loop adds the string you're composing with each iteration to a list of strings. Then, when you exit the loop (because i reaches searchLimit) that list of strings will contain N many, well, strings. You probably want to return or continue working that.
All that said, you can't (technically you can but you SHOULDN'T) do any of this inside a UI thread. If you do, the UI will block (and become 100% unresponsive to the user) while the loop runs (and the network calls that it makes run), etc.

Amazon Kinesis GetRecords Api Best approach

I have setup Kinesis stream in Amazon WebServices. I Also want to accomplish the following tasks:
Put Records into Single Stream with Single Shard (C# Api) - SUCCESS
Also I wrote Sample App in which multiple Producers are working on different Stream - SUCCESS
Also I setup Sample App to Perform Mutiple Workers put the Data into Single Stream - SUCCESS
Also I want to be able to enforce the SequenceNumberOrdering in the Reacords.
But the real pain is the GetRecords Consumer Operation using Kinesis C# Api.
I created a sample App for the Records. The problem is that it doesn't stop the Iteration even if there are no Records present in the Kinesis Stream. Also keeping the SequenceNumber in the DB or some file and retrieving the file again is time consuming - what is the advantage of using Kinesis Stream for GetRecords?
Why does it keep on iterating even when there is no data in the Stream?
I used following piece of code for the REFERENCE;
private static void GetFilesKinesisStream()
{
IAmazonKinesis kinesis = AWSClientFactory.CreateAmazonKinesisClient();
try
{
ListStreamsResponse listStreams = kinesis.ListStreams();
int numBuckets = 0;
if (listStreams.StreamNames != null &&
listStreams.StreamNames.Count > 0)
{
numBuckets = listStreams.StreamNames.Count;
Console.WriteLine("You have " + numBuckets + " Amazon Kinesis Streams.");
Console.WriteLine(string.Join(",\n", listStreams.StreamNames.ToArray()));
DescribeStreamRequest describeRequest = new DescribeStreamRequest();
describeRequest.StreamName = "******************";
DescribeStreamResponse describeResponse = kinesis.DescribeStream(describeRequest);
List<Shard> shards = describeResponse.StreamDescription.Shards;
foreach (Shard s in shards)
{
Console.WriteLine("shard: " + s.ShardId);
}
string primaryShardId = shards[0].ShardId;
GetShardIteratorRequest iteratorRequest = new GetShardIteratorRequest();
iteratorRequest.StreamName = "*********************";
iteratorRequest.ShardId = primaryShardId;
iteratorRequest.ShardIteratorType = ShardIteratorType.AT_SEQUENCE_NUMBER;
iteratorRequest.StartingSequenceNumber = "49544005271533118105145368110776211536226129690186743810";
GetShardIteratorResponse iteratorResponse = kinesis.GetShardIterator(iteratorRequest);
string iterator = iteratorResponse.ShardIterator;
Console.WriteLine("Iterator: " + iterator);
//Step #3 - get records in this iterator
GetShardRecords(kinesis, iterator);
Console.WriteLine("All records read.");
Console.ReadLine();
}
// sr.WriteLine("You have " + numBuckets + " Amazon S3 bucket(s).");
}
catch (AmazonKinesisException ex)
{
if (ex.ErrorCode != null && ex.ErrorCode.Equals("AuthFailure"))
{
Console.WriteLine("The account you are using is not signed up for Amazon EC2.");
Console.WriteLine("You can sign up for Amazon EC2 at http://aws.amazon.com/ec2");
}
else
{
Console.WriteLine("Caught Exception: " + ex.Message);
Console.WriteLine("Response Status Code: " + ex.StatusCode);
Console.WriteLine("Error Code: " + ex.ErrorCode);
Console.WriteLine("Error Type: " + ex.ErrorType);
Console.WriteLine("Request ID: " + ex.RequestId);
}
}
}
private static void GetShardRecords(IAmazonKinesis client, string iteratorId)
{
//create reqest
GetRecordsRequest getRequest = new GetRecordsRequest();
getRequest.Limit = 100;
getRequest.ShardIterator = iteratorId;
//call "get" operation and get everything in this shard range
GetRecordsResponse getResponse = client.GetRecords(getRequest);
//get reference to next iterator for this shard
string nextIterator = getResponse.NextShardIterator;
//retrieve records
List<Record> records = getResponse.Records;
//print out each record's data value
foreach (Record r in records)
{
//pull out (JSON) data in this record
string s = Encoding.UTF8.GetString(r.Data.ToArray());
Console.WriteLine("Record: " + s);
Console.WriteLine("Partition Key: " + r.PartitionKey);
}
if (null != nextIterator)
{
//if there's another iterator, call operation again
GetShardRecords(client, nextIterator);
}
}
Why does a kinesis consumer keep iterating after the "end" of the data?
Because there is no "end". Kinesis is sort of like a queue, but not exactly. Think of it like a moving time window of recorded events. You don't consume records, you examine records passively that are currently in the window (which amazon hardcodes to 24 hours). Because the window is always moving, once you reach the "last" record, it keeps watching in real time. New records could come at any time; the consumer doesn't know that there aren't any producers.
If you want to stop based on some condition, that condition has to be contained in your payload. For example, if you wanted to stop when you got to "now", part of your payload could be a timestamp, which the consumer checks for proximity to its current time.

Testing an Entity Framework database connection

I have an app that connects to a MYSQL database through the entity framework. It works 100% perfectly, but I would like to add a small piece of code that will test the connection to the database upon app startup.
I had the idea of simply running a tiny command to the database and catching any exceptions, however if there is a problem (eg App.Config missing or Database server down) the app takes a huge amount of time to run this code and then throw the exception (~1 min). I imagine this is due to connection timeouts etc but I have fiddled with such properties to no avail.
Would anyone be able to assist with any ideas as to where to go?
Are you just wanting to see if the DB connection is valid? If so take a look at the
using (DatabaseContext dbContext = new DatabaseContext())
{
dbContext.Database.Exists();
}
http://msdn.microsoft.com/en-us/library/gg696617(v=vs.103).aspx
and for checking if a server machine is up, DB server or web services server , try this:
public PingReply Send( string hostNameOrAddress )
http://msdn.microsoft.com/en-us/library/7hzczzed.aspx
The solution as #Danilo Breda pointed out is to call the DbContext.Database.Connection.Open()
It is tested with EF6.
My implementaion:
public static bool CheckConnection()
{
try
{
MyContext.Database.Connection.Open();
MyContext.Database.Connection.Close();
}
catch(SqlException)
{
return false;
}
return true;
}
In EntityFramework Core you can simply call: Database.CanConnect();.
(using EF Core 2.2.1)
Summary: Determines whether or not the database is available and can be connected to.
Note that being able to connect to the database does not mean that it is up-to-date with regard to schema creation, etc.
I use this code for my project:
private bool TestConnectionEF()
{
using (var db = new SistemaContext())
{
db.Database.Connection.Open();
if (db.Database.Connection.State == ConnectionState.Open)
{
Console.WriteLine(#"INFO: ConnectionString: " + db.Database.Connection.ConnectionString
+ "\n DataBase: " + db.Database.Connection.Database
+ "\n DataSource: " + db.Database.Connection.DataSource
+ "\n ServerVersion: " + db.Database.Connection.ServerVersion
+ "\n TimeOut: " + db.Database.Connection.ConnectionTimeout);
db.Database.Connection.Close();
return true;
}
return false;
}
}
I know this is an old question, but here is my answer for anyone looking for a newer implementation.
I was able to use CanConnect to check the status of the database:
_database.Database.CanConnect();
# Async too
await _database.Database.CanConnectAsync(_cancellationTokenSource.Token);
I hope this helps others as well. Cheers!
I used the answer from #Sandor and did an extension method to use with EntityFramework Core.
Here's the code:
using Microsoft.EntityFrameworkCore;
using System.Data.Common;
namespace TerminalInventory
{
public static class ExtensionMethods
{
public static bool TestConnection(this DbContext context)
{
DbConnection conn = context.Database.GetDbConnection();
try
{
conn.Open(); // Check the database connection
return true;
}
catch
{
return false;
}
}
}
}
Now you just have to call:
if (!context.TestConnection())
{
logger.LogInformation("No database connection. Check the connection string in settings.json. {0}", configuration["connectionString"]);
return;
}
I am using the following code for MS SQL connection. Maybe, it will be useful for MySQL too. You don’t even need to use an EF or EF Core.
public bool IsDbConnectionOK()
{
SqlConnectionStringBuilder conStr = new SqlConnectionStringBuilder
{
DataSource = ButtonServerName.Text, // <-- My Form Elements
InitialCatalog = ButtonDBName.Text, // <-- My Form Elements
UserID = EditUserName.Text, // <-- My Form Elements
Password = EditPassword.Text, // <-- My Form Elements
IntegratedSecurity = false,
ConnectTimeout = 30
};
string connectionstring = conStr.ToString();
try
{
using (System.Data.SqlClient.SqlConnection connection = new System.Data.SqlClient.SqlConnection(connectionstring))
{
connection.Open();
return true;
}
}
catch (System.Data.SqlClient.SqlException ex)
{
MessageBox.Show(ex.Message + Environment.NewLine +
"Error line: " + ex.LineNumber + Environment.NewLine +
"Procedure name: " + ex.Procedure);
return false;
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
return false;
}
}

Categories