|
First you need to design your banking system. You cannot create a transaction until you know what inputs it takes, what outputs it needs to produce, and what rules it must follow.
|
|
|
|
|
|
Hello experts,
I have five (5) public facing REST servers and I have been tasked with coming up with a script that will monitor the status of the servers (whether up or down) and send email notifications three times a day, at &30am, 12:30pm, and 3:30pm.
The email notification should either indicate whether the servers are up or down.
I have had issues thinking up a solution.
However, while googling, I found this article:
Repeat Task every N interval using Windows Service in C# and VB.Net[^]
I am trying to follow it and I have made some minor changes.
For instance, in appSettings, I changed the configuration to this:
<appSettings>
<add key ="Mode" value ="Daily"/>
<add key ="ScheduledTime" value ="24:00"/>
</appSettings>
I am not entirely sure that it is completely correct.
Then, I made this change:
if (mode == "DAILY")
{
scheduledTime = DateTime.Parse(System.Configuration.ConfigurationManager.AppSettings["ScheduledTime"]);
if(DateTime.Now().ToString("HH:mm") = "07:30" || DateTime.Now().ToString("HH:mm") = "12:30" || DateTime.Now().ToString("HH:mm") = "3:30"))
{
scheduledTime = scheduledTime.AddDays(1);
}
}
Do I still need the WriteToFile(...) which saves the server status to a text file?
Here is the complete script from that link:
public partial class Service1 : ServiceBase
{
public Service1()
{
InitializeComponent();
}
protected override void OnStart(string[] args)
{
this.WriteToFile("Simple Service started {0}");
this.ScheduleService();
}
protected override void OnStop()
{
this.WriteToFile("Simple Service stopped {0}");
this.Schedular.Dispose();
}
private Timer Schedular;
public void ScheduleService()
{
try
{
Schedular = new Timer(new TimerCallback(SchedularCallback));
string mode = ConfigurationManager.AppSettings["Mode"].ToUpper();
this.WriteToFile("Simple Service Mode: " + mode + " {0}");
DateTime scheduledTime = DateTime.MinValue;
if (mode == "DAILY")
{
scheduledTime = DateTime.Parse(System.Configuration.ConfigurationManager.AppSettings["ScheduledTime"]);
if(DateTime.Now().ToString("HH:mm") = "07:30" || DateTime.Now().ToString("HH:mm") = "12:30" || DateTime.Now().ToString("HH:mm") = "3:30"))
{
scheduledTime = scheduledTime.AddDays(1);
}
}
if (mode.ToUpper() == "INTERVAL")
{
int intervalMinutes = Convert.ToInt32(ConfigurationManager.AppSettings["IntervalMinutes"]);
scheduledTime = DateTime.Now.AddMinutes(intervalMinutes);
if (DateTime.Now > scheduledTime)
{
scheduledTime = scheduledTime.AddMinutes(intervalMinutes);
}
}
TimeSpan timeSpan = scheduledTime.Subtract(DateTime.Now);
string schedule = string.Format("{0} day(s) {1} hour(s) {2} minute(s) {3} seconds(s)", timeSpan.Days, timeSpan.Hours, timeSpan.Minutes, timeSpan.Seconds);
this.WriteToFile("Simple Service scheduled to run after: " + schedule + " {0}");
int dueTime = Convert.ToInt32(timeSpan.TotalMilliseconds);
Schedular.Change(dueTime, Timeout.Infinite);
}
catch(Exception ex)
{
WriteToFile("Simple Service Error on: {0} " + ex.Message + ex.StackTrace);
using (System.ServiceProcess.ServiceController serviceController = new System.ServiceProcess.ServiceController("SimpleService"))
{
serviceController.Stop();
}
}
}
private void SchedularCallback(object e)
{
this.WriteToFile("Simple Service Log: {0}");
this.ScheduleService();
}
private void WriteToFile(string text)
{
string path = "C:\\ServiceLog.txt";
using (StreamWriter writer = new StreamWriter(path, true))
{
writer.WriteLine(string.Format(text, DateTime.Now.ToString("dd/MM/yyyy hh:mm:ss tt")));
writer.Close();
}
}
}
Assistance, as always, is greatly appreciated.
|
|
|
|
|
Rather than trying to write your own task scheduler, use the built-in Windows task scheduler.
Task Scheduler for developers - Win32 apps | Microsoft Docs[^]
Create a simple console application which checks your servers, sends the email, and exits. Schedule it to run at whatever time(s) you want.
"These people looked deep within my soul and assigned me a number based on the order in which I joined."
- Homer
|
|
|
|
|
Hi Richard,
As always, thank you sir for your continued assistance.
I *think* this console app will do the trick:
using System.Net.NetworkInformation;
public static void ServerStatusBy(string url)
{
Ping pingSender = new Ping();
PingReply reply = pingSender.Send(url);
Console.WriteLine("Status of Host: {0}", url);
if (reply.Status == IPStatus.Success)
{
Console.WriteLine("IP Address: {0}", reply.Address.ToString());
Console.WriteLine("RoundTrip time: {0}", reply.RoundtripTime);
Console.WriteLine("Time to live: {0}", reply.Options.Ttl);
Console.WriteLine("Don't fragment: {0}", reply.Options.DontFragment);
Console.WriteLine("Buffer size: {0}", reply.Buffer.Length);
}
else
Console.WriteLine(reply.Status);
}
Then this will be the call:
Console.Write("Enter server url:");
var url = Console.ReadLine();
Console.Clear();
ServerStatusBy(url);
Console.ReadLine();
I have some questions please.
1, There are 5 API servers as indicated and I can schedule this to run on each server, fine.
However, can you help with not having to interact with the app?
We don't want to have to enter server url in order to get the status.
We just want the app to contact the URL automatically.
So could that mean something like this:
public static void ServerStatusBy(string url)
url = "https://www.something.com/REST" for example?
The second question that I have is that I can essentially write something like:
if (reply.Status == IPStatus.Success)
{
Console.WriteLine("Server APP One is running");
}
else
{
Console.WriteLine("Server APP One is running");
}
Once again, thank you very much sir for your help.
|
|
|
|
|
Just a thought; what if you pass the URL as a command line argument ? This way the program is agnostic to which server it is checking. You could also pass the email addresses on the command line.
|
|
|
|
|
Hi David and thanks a lot for your response.
When you say to pass the URL as command line argument, do you mean when scheduling the app to run?
This is all new for me unfortunately and I ask that you guys forgive me for the questions if they appear silly as probably are.
//UPDATE:
I tried changing the code to the one below so user doesn't have to be prompted to enter URL:
using System;
using System.Net.NetworkInformation;
using System.Text;
namespace showserverstatus
{
class Program
{
static void Main(string[] args)
{
}
public static void ServerStatusBy()
{
Ping pingSender = new Ping();
string data = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
byte[] buffer = Encoding.ASCII.GetBytes(data);
int timeout = 10000;
PingOptions options = new PingOptions(64, true);
PingReply reply = pingSender.Send("www.yahoo.com", timeout, buffer, options);
if (reply.Status == IPStatus.Success)
{
Console.WriteLine("IP Address: {0}", reply.Address.ToString());
Console.WriteLine("RoundTrip time: {0}", reply.RoundtripTime);
Console.WriteLine("Time to live: {0}", reply.Options.Ttl);
Console.WriteLine("Don't fragment: {0}", reply.Options.DontFragment);
Console.WriteLine("Buffer size: {0}", reply.Buffer.Length);
}
else
Console.WriteLine(reply.Status);
}
}
I got the following:
Press any key to continue...
When I do, the console closes.
No records shows.
modified 24-Aug-21 23:19pm.
|
|
|
|
|
Any help with this please?
I know this is not your issue but I am being pressured for completion of this task.
Many thanks in advance.
|
|
|
|
|
I see a few problems with your code:
1) Looks like your main() does not call your ServerStatusBy() function
2) You are not reading any values from the command line.
Here is a VB example:
Sub Main()
Dim args() As String = System.Environment.GetCommandLineArgs()
Dim s As String
For i As Integer = 0 To args.Length - 1
Console.WriteLine(String.Format("Arg {0}): {1} ", i, args(i)))
Next
s = Console.ReadLine
End Sub
When you run the program from a command prompt like HellowWorld.exe www.google.com jdoe@companyabc.com
The output is as follows:
Arg 0): C:\_WorkingCopies\HelloWorld\HelloWorld\bin\Debug\HelloWorld.vshost.exe
Arg 1): www.google.com
Arg 2): jdoe@companyabc.com
You can see that you want to grab Arg(1) as the URL and Arg(2) as the email address you want to send the report.
|
|
|
|
|
David,
Thank you so much for your help.
I converted your VB to C# and this is my code after integrating yours but still doesn't work.
I am also getting this: Unnecessary assignment of a value to 's'
Just so you know, I am not limited to using only C# for this task.
I can use VB if that's available.
Thank you again for your help.
using System;
using System.Net.NetworkInformation;
using System.Text;
namespace showserverstatus
{
class Program
{
static void Main()
{
string[] args = System.Environment.GetCommandLineArgs();
string s;
for (int i = 0; i <= args.Length - 1; i++)
Console.WriteLine(string.Format("Arg {0}): {1} ", i, args[i]));
s = Console.ReadLine();
}
public static void ServerStatusBy()
{
Ping pingSender = new Ping();
int timeout = 10000;
PingReply reply = pingSender.Send("www.yahoo.com", timeout);
if (reply.Status == IPStatus.Success)
{
Console.WriteLine("IP Address: {0}", reply.Address.ToString());
Console.WriteLine("RoundTrip time: {0}", reply.RoundtripTime);
Console.WriteLine("Time to live: {0}", reply.Options.Ttl);
Console.WriteLine("Don't fragment: {0}", reply.Options.DontFragment);
Console.WriteLine("Buffer size: {0}", reply.Buffer.Length);
}
else
Console.WriteLine(reply.Status);
}
}
}
|
|
|
|
|
You can get the command-line arguments by changing the declaration of your Main method to Main(string[] args) .
You don't want to call Console.ReadLine if you're going to schedule your application to run in the background. There won't be a user to press a key and end the application. Instead, return an exit code indicating the status of your application - 0 generally indicates success, and anything else indicates an error.
You'll want to pass each command-line argument to your status function, and use that as the site to test - for example:
static int Main(string[] args)
{
int result = 0;
foreach (string site in args)
{
if (!ServerStatusBy(site))
{
result++;
}
}
return result;
}
static bool ServerStatusBy(string site)
{
Ping pingSender = new Ping();
PingReply reply = pingSender.Send(site, 10000);
if (reply.Status != IPStatus.Success)
{
Console.WriteLine("{0}: {1}", site, reply.Status);
return false;
}
...
return true;
}
YourApp.exe site1.local codeproject.com google.com
"These people looked deep within my soul and assigned me a number based on the order in which I joined."
- Homer
|
|
|
|
|
WOW, thank you very much sir.
You remind me so much of this super guru called Bill Wilkinson of classic ASP and JavaScript way back when I was learning classic ASP.
Just when I felt stuck and in trouble, he would come out of nowhere to help me out.
God bless you sir.
Please forgive me for additional questions.
1, does this ... (the dot dot dot) mean there is more code to go in there?
2, I use this -> YourApp.exe site1.local codeproject.com google.com for scheduling the run times, correct?
3, This -> YourApp.exe site1.local codeproject.com google.com means that I can add the five links all at once correct?
Many thanks again. I really appreciate your help.
I feel a bit better now.
UPDATE: I think I answered question #1.
This is how I *THINK* the code should look like now.
I added the email component since users will be getting emails with the status of the servers whether up or down.
Email subject should indicate Server Up or Down bases on what happens on ServerStatusBy() method
using System;
using System.IO;
using System.Net;
using System.Net.Mail;
using System.Net.NetworkInformation;
using System.Text;
using System.Configuration;
namespace showserverstatus
{
class Program
{
private string statusMessage;
static int Main(string[] args)
{
int result = 0;
foreach (string site in args)
{
if (!ServerStatusBy(site))
{
result++;
}
}
return result;
}
static bool ServerStatusBy(string site)
{
Ping pingSender = new Ping();
PingReply reply = pingSender.Send(site, 10000);
if (reply.Status != IPStatus.Success)
{
Console.WriteLine("{0}: {1}", site, reply.Status);
string statusMessage = "Shw message that Server is down";
SendEmail();
return false;
}
else
statusMessage = "Shw message that Server is up";
Console.WriteLine("IP Address: {0}", reply.Address.ToString());
Console.WriteLine("RoundTrip time: {0}", reply.RoundtripTime);
Console.WriteLine("Time to live: {0}", reply.Options.Ttl);
Console.WriteLine("Don't fragment: {0}", reply.Options.DontFragment);
Console.WriteLine("Buffer size: {0}", reply.Buffer.Length);
SendEmail();
return true;
}
public static void SendEmail()
{
using (MailMessage mm = new(ConfigurationManager.AppSettings["FromEmail"],"joe.bloew@yahoo.com"))
{
mm.To.Add("joesixpack@gmail.com");
mm.CC.Add("janeDoe@gmail.com");
mm.Subject = statusMessage;
mm.Body = "Test";
mm.IsBodyHtml = false;
SmtpClient smtp = new SmtpClient();
smtp.Host = ConfigurationManager.AppSettings["Host"];
smtp.EnableSsl = true;
NetworkCredential NetworkCred = new NetworkCredential(ConfigurationManager.AppSettings["Username"], ConfigurationManager.AppSettings["Password"]);
smtp.UseDefaultCredentials = true;
smtp.Credentials = NetworkCred;
smtp.Port = int.Parse(ConfigurationManager.AppSettings["Port"]);
Console.WriteLine("Sending Email......");
smtp.Send(mm);
Console.WriteLine("Email Sent.");
System.Threading.Thread.Sleep(3000);
Environment.Exit(0);
}
}
}
}
modified 25-Aug-21 19:11pm.
|
|
|
|
|
1. It looks like you've already answered that one.
2. Yes, you'd set the application to be the full path to your .exe file, and the command-line arguments to be the list of domains you want to test.
3. You can pass as many domains to test as you want as command-line arguments.
samflex wrote:
if (reply.Status != IPStatus.Success)
{
Console.WriteLine("{0}: {1}", site, reply.Status);
string statusMessage = "Shw message that Server is down";
SendEmail();
return false;
}
else
statusMessage = "Shw message that Server is up"; A couple of problems there:
Within the if block, you've created a local variable called statusMessage , which hides the static field of the same name. The value you store in the local variable won't be visible to the SendEmail function.
You don't need the else , since you've got a return within the if block.
samflex wrote:
Console.WriteLine("Email Sent.");
System.Threading.Thread.Sleep(3000);
Environment.Exit(0); You shouldn't call Environment.Exit within the SendEmail function, since that will terminate your app after the first message is sent.
I'd avoid using a field, and pass the message subject and body as parameters to the SendEmail function instead.
public static void SendEmail(string subject, string body)
{
using MailMessage mm = new(ConfigurationManager.AppSettings["FromEmail"], "joe.blow@yahoo.com");
mm.To.Add("joesixpack@gmail.com");
mm.CC.Add("jandoe@gmail.com");
mm.Subject = subject;
mm.Body = body;
mm.IsBodyHtml = false;
SmtpClient smtp = new()
{
Host = ConfigurationManager.AppSettings["Host"],
Port = int.Parse(ConfigurationManager.AppSettings["Port"]),
EnableSsl = true,
UseDefaultCredentials = false,
Credentials = new NetworkCredential(ConfigurationManager.AppSettings["Username"], ConfigurationManager.AppSettings["Password"]),
};
Console.WriteLine("Sending email...");
smtp.Send(mm);
Console.WriteLine("Email sent.");
System.Threading.Thread.Sleep(3000);
}
static bool ServerStatusBy(string site)
{
Ping pingSender = new();
PingReply reply = pingSender.Send(site, 10000);
if (reply.Status != IPStatus.Success)
{
Console.WriteLine("{0}: {1}", site, reply.Status);
SendEmail($"{site} DOWN", $"Ping {site} returned {reply.Status}.");
return false;
}
Console.WriteLine("IP Address: {0}", reply.Address);
Console.WriteLine("RoundTrip time: {0}", reply.RoundtripTime);
Console.WriteLine("Time to live: {0}", reply.Options.Ttl);
Console.WriteLine("Don't fragment: {0}", reply.Options.DontFragment);
Console.WriteLine("Buffer size: {0}", reply.Buffer.Length);
SendEmail($"{site} Up", $@"Ping {site}
IP Address: {reply.Address}
RoundTrip time: {reply.RoundtripTime}
Time to live: {reply.Options.Ttl}
Don't fragment: {reply.Options.DontFragment}
Buffer size: {reply.Buffer.Length}");
return true;
}
"These people looked deep within my soul and assigned me a number based on the order in which I joined."
- Homer
|
|
|
|
|
Man, looks like you were born with this
I work hard and spend a lot of time learning this but still struggle mightily.
Many, many thanks sir.
I am so grateful as this solution comes in handy as they are demanding demo by 10am togay.
Just need one more clarification, is this right?
YourApp.exe site1.local codeproject.com google.com
|
|
|
|
|
samflex wrote: YourApp.exe site1.local codeproject.com google.com
That's correct, if your compiled application is YourApp.exe , and you want to test three domains - site1.local , codeproject.com , and google.com .
Obviously you'd need to use your real .exe name there, and pass in the actual domains / servers you wanted to test.
"These people looked deep within my soul and assigned me a number based on the order in which I joined."
- Homer
|
|
|
|
|
Oh ok. I was confused by site1 thinking it was meant to be site as per the argument in ServerStatusBy() method.
Thank God my demo pushed to 1:30pm so i can test this to see what happens.
Thank you so much.
I can't say this enough.
|
|
|
|
|
I have run into a little problem here.
Console app, at least the one I am building and compiling is not generating an .exe file.
For instance, the project name is showserverstatus.
When I go into the bin folder, debug, I see showserverstatus.dll but not .exe
As a result, I am having problem testing the app.
Any ideas sir?
|
|
|
|
|
Looks like you've created a .NET Core / .NET 5 application. You would need to publish the application to generate an exe file:
Build .NET Core console application to output an EXE - Stack Overflow[^]
Otherwise, you would need run it using the dotnet command:
dotnet showserverstatus.dll site1 site2 ...
Or you could change the target framework to net472 to generate a .NET Framework exe instead. Double-click on the project file, find the line that looks like:
<TargetFramework>net5.0<TargetFramework> and change it to:
<TargetFramework>net472</TargetFramework> Target frameworks in SDK-style projects - .NET | Microsoft Docs[^]
"These people looked deep within my soul and assigned me a number based on the order in which I joined."
- Homer
|
|
|
|
|
Sorry for all the trouble sir.
Please hang in there with me a little longer if you can.
I chose the path of least resistance by publishing and like you correctly stated, it generated .exe file.
I tried testing and ran into an error.
Please see how I was testing and the error:
C:\inetpub\wwwroot\showserverstatus\bin\publish>showserverstatus.exe google.com
IP Address: 2607:f8b0:4000:819::200e
RoundTrip time: 44
Unhandled exception. System.NullReferenceException: Object reference not set to an instance of an object.
at showserverstatus.Program.ServerStatusBy(String site) in C:\inetpub\wwwroot\showserverstatus\Program.cs:line 41
at showserverstatus.Program.Main(String[] args) in C:\inetpub\wwwroot\showserverstatus\Program.cs:line 16
As you can see, it displayed status of two lines before the error.
This is line 16:
foreach (string site in args)
and this is line 41:
Console.WriteLine("Time to live: {0}", reply.Options.Ttl);
UPDATE:
My demo went well for the mere fact that this app actually ran and sent email was considered a success even though the email did not contain anything.
Here is why.
Due to the error I was getting that I posted above, I removed all of those reply.stuff and then just left only DOWN or WORKING.
They wanted them worded as those.
Per the changes I made below, the app ran and sent the email but when I received the email, there was no message that site is WORKING or DOWN.
Is it because of the changes by replacing Subject with hardcoded values and body with hardcoded values?
They also brought up something that I overlooked.
They wanted Subject to read: Server Status
They wanted the body to read:
Please find the status of the DMZ servers below:
whatever site name is - working or Down (Whatever the status is).
This is latest code.
Please, please forgive me sir for all these troubles. Looks like we are almost there.
I changed the code below to format them just as described above.
The only issue now and it is a big one is that it works with pretty much any URL I tested with except anything that begins with gis.
For instance, none of these worked:
https://gis.massdot.state.ma.us/arcgis/rest
gis.massdot.state.ma.us/arcgis/rest/
gis.massdot.state.ma.us/arcgis/rest
using System;
using System.IO;
using System.Net;
using System.Net.Mail;
using System.Net.NetworkInformation;
using System.Text;
using System.Configuration;
using System.Collections.Generic;
namespace showserverstatus
{
class Program
{
static void Main(string[] args)
{
var siteToStatus = new Dictionary<string, string>();
foreach (var site in args)
{
var reply = new Ping().Send(site, 10000);
siteToStatus[site] = (reply.Status == IPStatus.Success) ? "UP" : "DOWN";
}
var subject = "Server Status";
var body = "Please find the status of the servers below:";
foreach (var kvp in siteToStatus)
{
body += $"{Environment.NewLine}{kvp.Key}: {kvp.Value}";
}
SendEmail(subject, body);
}
static bool ServerStatusBy(string site)
{
Ping pingSender = new();
PingReply reply = pingSender.Send(site, 10000);
if (reply.Status != IPStatus.Success)
{
SendEmail($"{site} DOWN", $"Ping {site}");
return false;
}
SendEmail($"{site} WORKING", $@"Ping {site}");
return true;
}
public static void SendEmail(string subject, string body)
{
using MailMessage mm = new(ConfigurationManager.AppSettings["FromEmail"], "joeblow@yahoo.com");
mm.To.Add("johndoe@gmail.com");
mm.CC.Add("joesixpack@yahoo.com");
mm.Subject = subject;
mm.Body = body;
mm.IsBodyHtml = false;
SmtpClient smtp = new()
{
Host = ConfigurationManager.AppSettings["Host"],
Port = int.Parse(ConfigurationManager.AppSettings["Port"]),
EnableSsl = true,
UseDefaultCredentials = false,
Credentials = new NetworkCredential(ConfigurationManager.AppSettings["Username"], ConfigurationManager.AppSettings["Password"]),
};
Console.WriteLine("Sending email...");
smtp.Send(mm);
Console.WriteLine("Email sent.");
System.Threading.Thread.Sleep(3000);
}
}
}
modified 26-Aug-21 23:19pm.
|
|
|
|
|
Looks like the reply.Options isn't set. You will need to test for null , or use a null-conditional operator:
if (reply.Options != null)
{
Console.WriteLine("Time to live: {0}", reply.Options.Ttl);
Console.WriteLine("Don't fragment: {0}", reply.Options.DontFragment);
}
...
SendEmail($"{site} Up", $@"Ping {site}
IP Address: {reply.Address}
RoundTrip time: {reply.RoundtripTime}
Time to live: {reply.Options?.Ttl}
Don't fragment: {reply.Options?.DontFragment}
Buffer size: {reply.Buffer?.Length}");
samflex wrote: For instance, none of these worked:
https://gis.massdot.state.ma.us/arcgis/rest
gis.massdot.state.ma.us/arcgis/rest/
gis.massdot.state.ma.us/arcgis/rest
You're pinging a server, not a URL. It looks like gis.massdot.state.ma.us either isn't up, or is blocking pings.
If you actually want to test a URL, you'd need to use a different approach. For example:
static async Task<int> Main(string[] args)
{
System.Collections.Concurrent.ConcurrentDictionary<string, string> urlToStatus = new();
IEnumerable<Task<bool>> tasks = args.Select(async url =>
{
bool result = await ServerStatusByAsync(url);
urlToStatus.TryAdd(url, result ? "UP" : "DOWN");
});
await Task.WhenAll(tasks);
StringBuilder body = new("Please find the status of the servers below:");
foreach (var kvp in urlToStatus)
{
body.AppendLine();
body.AppendFormat("{0}: {1}", kvp.Key, kvp.Value);
}
await SendEmailAsync("Server Status", body.ToString());
await Task.Delay(3000);
}
static async Task<bool> ServerStatusByAsync(string url)
{
HttpClient http = new();
using (HttpResponseMessage response = await http.GetAsync(url))
{
Console.WriteLine("GET {0}: {1}", url, response.StatusCode);
if (response.IsSuccessStatusCode)
{
await SendEmailAsync($"{url} WORKING", $"GET {url} returned {response.StatusCode}");
return true;
}
await SendEmailAsync($"{url} DOWN", $"GET {url} returned {response.StatusCode}");
return false;
}
}
static async Task SendEmailAsync(string subject, string body)
{
using MailMessage mm = new(ConfigurationManager.AppSettings["FromEmail"], "joeblow@yahoo.com");
mm.To.Add("johndoe@gmail.com");
mm.CC.Add("joesixpack@yahoo.com");
mm.Subject = subject;
mm.Body = body;
mm.IsBodyHtml = false;
SmtpClient smtp = new()
{
Host = ConfigurationManager.AppSettings["Host"],
Port = int.Parse(ConfigurationManager.AppSettings["Port"]),
EnableSsl = true,
UseDefaultCredentials = false,
Credentials = new NetworkCredential(ConfigurationManager.AppSettings["Username"], ConfigurationManager.AppSettings["Password"]),
};
await smtp.SendMailAsync(mm);
} This has the added advantage of testing all URLs at once, rather than one-by-one, which should reduce the time it takes to test multiple URLs.
"These people looked deep within my soul and assigned me a number based on the order in which I joined."
- Homer
|
|
|
|
|
My goodness, how did you learn all of these?
WOW.
Just a couple of things sir,
One, I got this error:
Error CS0161 'Program.Main(string[])': not all code paths return a value
I am assuming that all I needed to do was copy your latest code and replace from Main all the way down.
Second, runing it is still the same?
That is, showserverstatus.exe google.com yahoo.com gis..../rest/, etc?
I am truly in awe and so sorry for continuing to ask questions about this.
|
|
|
|
|
Sorry, missed the return statement:
static async Task<int> Main(string[] args)
{
System.Collections.Concurrent.ConcurrentDictionary<string, string> urlToStatus = new();
IEnumerable<Task<bool>> tasks = args.Select(async url =>
{
bool result = await ServerStatusByAsync(url);
urlToStatus.TryAdd(url, result ? "UP" : "DOWN");
});
bool[] results = await Task.WhenAll(tasks);
StringBuilder body = new("Please find the status of the servers below:");
foreach (var kvp in urlToStatus)
{
body.AppendLine();
body.AppendFormat("{0}: {1}", kvp.Key, kvp.Value);
}
await SendEmailAsync("Server Status", body.ToString());
await Task.Delay(3000);
return results.Count(result => !result);
}
Running it is still the same, except you now need to pass a list of URLs to test, rather than a list of server names to ping.
"These people looked deep within my soul and assigned me a number based on the order in which I joined."
- Homer
|
|
|
|
|
Oh man!
Getting the following error:
Unhandled exception. System.InvalidCastException: Unable to cast object of type 'SelectArrayIterator`2[System.String,System.Threading.Tasks.Task]' to type 'System.Collections.Generic.IEnumerable`1[System.Threading.Tasks.Task`1[System.Boolean]]'.
It says line 21 which is this line:
IEnumerable> tasks = (IEnumerable>)args.Select(async url =>
This is the second line in Main().
Just for clarity sir, this is your version:
IEnumerable < Task<bool> > tasks = args.Select(async url =>
However, when I ran it, I got:
CS0266 Cannot implicitly convert type 'System.Collections.Generic.IEnumerable' to 'System.Collections.Generic.IEnumerable>'. An explicit conversion exists (are you missing a cast?)
When I selected the suggested explicit cast option, the error went away but then I got the other error during testing.
The error occurred when I tried passing one URL as an argument to test like:
showserverstatus.exe google.com
UPDATE: It's always something!!!
I got the error worked out; just took me awhile to figure out what was missing - return...
Now, email is not working. Not getting sent.
UPDATE: Once again, I panicked and didn't calm down to look for solutions.
Now, it is working. It turns out that I added myemail.com.net which obviously will make it hard to receive an email.
After correcting it, everything is fine now.
My Lord, it is impossible to express my most heartfelt gratitude for your patience, incredible ingenuity and most of all, you don't insult me.
Thank you very much sir, THANK YOU!!!
modified 27-Aug-21 14:29pm.
|
|
|
|
|
Hi everyone
We host CodeProject on dedicated servers in a very grown up (read: expensive) hosting centre that has all the bells and whistles. This is great, but for a friend who's asking about cheap ASP.NET hosting I'm kind of at a loss as to what to recommend.
What are your thought?
Requirements:
- Must be able to host ASP.NET Core / .NET 5
- Must include SQL Server, MySQL or postgres (at least 10Gb)
- Doesn't need much CPU
- Doesn't need much disk storage
- Needs fast internet
- Must be affordable ($10 a month or less)
I've (personally) used Winhost but I can't recommend then. They are easy to use, have everything in the above list except speed. Their bandwidth is pretty awful.
What are your thoughts?
cheers
Chris Maunder
|
|
|
|
|
I'll toss something into the basket here Chris.
Most developers went cloud or Azure, AWS Amazon and Google.
They even went cloud with CDN to call CSS, Fonts, etc.
I looked into pricing once, you can check off boxes for SQL server, MySQL, Cosmos, Mongo
Choose how much bandwidth you want, bandwidth on demand.
But it seems to really boil down to how you package your project and how little it's used. The average seems to be 7 to 13 cents a hour which is about $50.40 to $93.60 a month max. I'm not sure if the price represents active use of the container or app, and you don't pay when the container or app is idle.
Container pricing - where your container just floats in the system and gets served.
Pricing - Container Instances | Microsoft Azure
Traditional pricing including virtual machines
App Service Pricing | Microsoft Azure
But considering the price of electricity, service techs, support, cost of software licensing, and profit there is no way in 2021 can you do this for $10 a month in western democracies using that technology unless you have a friend that gives you a solid. I would try looking in places with cheap electricity and fast internet connections like Moldova, Romania, Ukraine, Southern Texas, Central China, Bulgaria, Montenegro, Croatia. I'm not kidding.
On the other hand, you can host a Wordpress, Joomla, PHP app for dirt cheap, as low as $1 a month I've seen anywhere in the world. It's the price of the technology, application packaging, electricity that plays a huge factor in price.
Haven't talk to you in a long time. I made lots of money in the last 18 months off being a rogue programmer and changing my business model and advertising. Finally bought a car after 18 years of doing this, picked up a really clean used CPO 2018 Porsche Macan GTS with all the power train toys and low miles. There's good money to be made in PHP, but not much help with it.
If it ain't broke don't fix it
Discover my world at jkirkerx.com
|
|
|
|
|