I was wondering if anyone knows a good way to regulate how many emails are sent through C#?
Here is my scenario. I have a windows service that monitors three other windows services. I am using a service controller to get the status of all 3 services and if the status of any of these services change to stopped, it sends an email. My issue is, I run this on a 60 second timer so it sends an email every 60 seconds until someone starts the service back up.
My first thought was, when first email is sent, create a text file and use a counter to number it. Do this while counter < 6 so I will only receive 5 emails max. I think this will work but it seems kind of silly.
Does anyone have an alternative to this or should I just go with my first thought and perform clean up on the files?
Thank you
EDIT: The reason that I was trying to limit the number of emails sent is because the people who receive these emails do not react very quickly. At the same time, those who handle Exchange do not want the service to spam people. I felt 5 would be enough to appease both sides.
I would suggest that you should track the down time of each service.
So every 60 seconds you check, if a service is down, store the DateTime that the service is down. The on the next 60 second interval you can check to see if the service was already down. i.e. you can tell if the service just went down or has been down a while. You can also add another flag to determine if the the last check was UP or DOWN.
Then when the program first finds the service down it can send the email. Once the service is back up it can reset this flag values so the next down time it knows to send a new email.
You can then also use these flags to delay email frequency if desired. Just add a new DateTime field for LastEmailSentTime and compare that with whatever interval you want for error emails (say 10 minutes)
Hope that gives you some ideas
EDIT: Some Sample...
bool ServiceWasDown = false;
DateTime ServiceDownTime = DateTime.Now;
DateTime LastEmailTime = DateTime.Now;
void OnTimerElapsed()
{
if(IsServiceDown())
ServiceDown();
else
ServiceUp();
}
void ServiceDown()
{
if(ServiceWasDown)//already know about service
{
//if LastEmailTime more than 10 minutes ago send another email and update LastEmailTime
}
else//service just went down
{
//send new email
LastEmailTime = DateTime.Now;
ServiceWasDown = true;
ServiceDownTime = DateTime.Now;
}
}
void ServiceUp()
{
ServiceWasDown = false;
}
If you use a System.Timers.Timer then You can add a int variable for count Elapsed events.
Related
Basically what I have to do is create a thread that executes in a given number of days, which can change. The point is that an email should be sent periodically, and I'm looking for the best approach eventhough I know this is definitely not one of the best solutions but I must continue with this per requirements, hence I cannot use Windows Services, Windows Scheduler or anything similar. So the starting day for this email to be sent is when app starts, and then the next time should be after X days as specified in web.config. The problem is that I don't know how to keep track of that if the application stops (I can save the date of application start in database) but the number of days for the next execution may change. This is what I've done so far:
private async Task<int> EmailScheduler()
{
var day = DateTime.Now.Day; //day when app starts
while (true)
{
var currentTime = DateTime.Now;
if (currentTime.Day == day)
{
SendEmail("mailto", "", "", "subject", EmailMessage());
day += Int32.Parse(ConfigurationManager.AppSettings["NumberOfDays"]); //next schedule
}
await Task.Delay((int)TimeSpan.FromDays(1).TotalMilliseconds);
}
}
Thanks in advance.
You have no control over when your application pool restarts, and it will most certainly not be running for extended periods of time.
You could save the last execution date in a database, and periodically poll it to see if it has been longer than your given number of days.
The polling frequency would depend on your requirements.
This would somewhat approach sending a mail "every few days", but it would probably not be as accurate as a scheduled task.
I am working on an assignment in asp.net to send notification email to users at specific intervals.
But the problem is that since the server is not privately owned i cannot implement a windows service on it.
Any ideas?
There's no reliable way to achieve that. If you cannot install a Windows Service on the host you could write a endpoint (.aspx or .ashx) that will send the email and then purchase on some other site a service which will ping this endpoint at regular intervals by sending it HTTP request. Obviously you should configure this endpoint to be accessible only from the IP address of the provider you purchase the service from, otherwise anyone could send an HTTP request to the endpoint and trigger the process which is probably undesirable.
Further reading: The Dangers of Implementing Recurring Background Tasks In ASP.NET.
There are several ways to get code executing on an interval that don't require a windows service.
One option is to use the Cache class - use one of the Insert overloads that takes a CacheItemRemovedCallback - this will be called when the cache item is removed. You can re-add the cache item with this callback again and again...
Though, the first thing you need to do is contact the hosting company and find out if they already have some sort of solution for you.
You could set up a scheduled task on the server to invoke a program with the desired action.
You can always use a System.Timer and create a call at specific intervals. What you need to be careful is that this must be run one time, eg on application start, but if you have more than one pools, then it may run more times, and you also need to access some database to read the data of your actions.
using System.Timers;
var oTimer = new Timer();
oTimer.Interval = 30000; // 30 second
oTimer.Elapsed += new ElapsedEventHandler(MyThreadFun);
oTimer.Start();
private static void MyThreadFun(object sender, ElapsedEventArgs e)
{
// inside here you read your query from the database
// get the next email that must be send,
// you send them, and mark them as send, log the errors and done.
}
why I select system timer:
http://msdn.microsoft.com/en-us/magazine/cc164015.aspx
more words
I use this in a more complex class and its work fine. What are the points that I have also made.
Signaling the application stop, to wait for the timer to end.
Use mutex and database for synchronize the works.
Easiest solution is to exploit global.asax application events
On application startup event, create a thread (or task) into a static singleton variable in the global class.
The thread/task/workitem will have an endless loop while(true) {...} with your "service like" code inside.
You'll also want to put a Thread.Sleep(60000) in the loop so it doesn't eat unnecessary CPU cycles.
static void FakeService(object obj) {
while(true) {
try {
// - get a list of users to send emails to
// - check the current time and compare it to the interval to send a new email
// - send emails
// - update the last_email_sent time for the users
} catch (Exception ex) {
// - log any exceptions
// - choose to keep the loop (fake service) running or end it (return)
}
Thread.Sleep(60000); //run the code in this loop every ~60 seconds
}
}
EDIT Because your task is more or less a simple timer job any of the ACID type concerns from an app pool reset or other error don't really apply, because it can just start up again and keep trucking along with any data corruption. But you could also use the thread to simply execute a request to an aspx or ashx that would hold your logic.
new WebClient().DownloadString("http://localhost/EmailJob.aspx");
Here I below pasted code I'm using in current window service solution, this will run job every day 10 AM regularly, I will pass the parameters using App.configuaration file
APP.CONFIG
<add key ="FIREHOST_TIME" value ="10" ></add>
<add key ="SETDAYS" value ="1" ></add>
CODE BEHIND PAGE
protected override void OnStart(string[] args)
{
DateTime tenAM = DateTime.Today.AddHours(FIREHOST_TIME);
if (DateTime.Now > tenAM)
tenAM = tenAM.AddDays(SETDAYS);
// calculate milliseconds until the next 10:00 AM.
int timeToFirstExecution = (int)tenAM.Subtract(DateTime.Now).TotalMilliseconds;
// calculate the number of milliseconds in 24 hours.
int timeBetweenCalls = (int)new TimeSpan(24, 0, 0).TotalMilliseconds;
TimerCallback methodToExecute = kickstart;
// start the timer. The timer will execute "ProcessFile" when the number of seconds between now and
// the next 10:00 AM elapse. After that, it will execute every 24 hours.
System.Threading.Timer timer = new System.Threading.Timer(methodToExecute, null, timeToFirstExecution, timeBetweenCalls);
}
Now I am trying to run my service, based up on the below Mentioned conditions:
I want to start my service but it should job perform the job based on the this new tag which I will add newly in app.config
BY based on above four tags
if RUN_NOW == 1
has to perform service based on FIREHOST_TIME and SETDAYS normal thing
else
service have to perform the Job doing by after 5 days(because WAIT_DAYS = 5)
then it have to use the FIREHOST_TIME and SETDAYS value
Note: service should not get stopped, it should be in started condition only
How can I achieve this?
I don't know exactly how you want implement the whole program logic, the portion I can try to help you is about reacting to a config file modification without restarting the service, this can be done with:
ConfigurationManager.RefreshSection("thesectiontorefresh");
for a better work, you can call this with a FileSystemWatcher listening for someone modifying the app.config file and react properly after calling the refresh as show above. You can obtain the configuration file path with this:
AppDomain.CurrentDomain.SetupInformation.ConfigurationFile
As a general information, consider to redesign the way you are solving the problem by using system scheduled task instead of writing your own service.
I have webpage with a button. when user pressing on button he get email with the current time. i need to send him another message after 60 sec async .. [he can still press the button how many times he wants]
how can i do it ?
after looking it up here is the answer
// Wait X Sec and then try to send message
System.Timers.Timer t = new System.Timers.Timer();
t.Interval = Convert.ToDouble(ConfigurationManager.AppSettings["timer"]);
t.Elapsed += delegate { /*your function here */};
t.Start();
t.AutoReset = false; // do it once
You can do it in the client side, as #Holystream suggested, but that will fail if the user leaves your page in thsoe 60 seconds, let alone close the browser.
If you don't want to create your own queuing mechanism, you can try using the Windows Scheduler. Just create a task that will access a URL in your application that will send that email. It'll work with you don't have a lot of such requests, and if the minute is not absolutely crucial (you can miss it a little, the scheduler isn't designed for such resolutions).
You should store data for schedule in DB with schedule time-stamp.
If user press button multiple times that time you can validate in DB if a scheduled email is already exists for the criteria ignore it.
You need to set Cron Job (Scheduler) service to call you page which will send emails from the data in DB if scheduled time is elapsed.
Based on accuracy you need, you need to set shorter duration for Cron Job to send emails.
If you want to set own scheduler service one good C# library is http://csharp-source.net/open-source/job-schedulers/quartz-net.
Hope this helps.
Previously I posted a question regarding multithreading. Actually my intension is to send SMS for 1000 (or more) people at a same point of time (Ex: 12:00 AM sharp) by using c# and asp.net application. Is it ok to choose multithreading concept to achieve this?
That concept do not need Multi Threading ...
That concept is more of a Task Manager / Cron Job
Create an ASPX Script that sees the time and executes the method you need
Set up Task Manager to run this script every xx minutes
Create a method, that fetches the list of persons and send the SMS through an SMS API, and call it, for ex. SendSMSFromList( List usersList, string message ) {}
Now set everything up and you will run this anytime you need (just set it in the ASPX Script)
please, fell free to tell me, if you need any code for this.
edited for having all steps
If you have a hosted solution, in your hosting control panel you have something as Task Schedule that you can set up to run your script page every n minutes, if so please by pass the next steps. If, by other hand, you are running your own server (IIS) then do this first.
Install cUrl for windows from this location and add curl.exe to C:\WINDOWS
Open Task Manager (Control Panel > Administrative Tools > Task Scheduler on win7)
Create a new task like this
Run the command
curl http://localhost/yourApp/taskManager.aspx
with this you just configured your system to run a file, just like if you execute that link in a browser, that will run every 15 minutes.
Now we need to create that taskManager.aspx file
public partial class taskManager : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
DateTime dt = DateTime.Now;
// Run after midnight
if (dt.Hour == 0 && dt.Minute <= 15)
{
Write2Log("Schedule Job Started", LogType.INFO);
SendSMSFromList(
GetUsersList(),
GetSMSMessage());
Write2Log("Schedule Job Finished", LogType.INFO);
}
}
private string GetSMSMessage()
{
// Fetch the text from DB...
return "This is the message content that I will send as SMS";
}
private List<string> GetUsersList()
{
// fetch the list form DB...
return new List<string>();
}
private void SendSMSFromList(List<string> usersList, string message)
{
// send SMS's
foreach (string phoneNr in usersList)
{
// send message
mySMSAPI.Send(phoneNr, message);
}
}
private void Write2Log(string text, LogType type)
{
// Log to a file what's going on...
try
{
string filename = HttpContext.Current.Server.MapPath("") + "\\status.log";
using (StreamWriter sw = new StreamWriter(filename, true)) // open to append
{
// example: 2008-12-17 13:53:10,462 INFO - Schedule Job Finished
string write = String.Format("{0} {1} - {2}",
DateTime.Now,
type.ToString(),
text);
sw.WriteLine(write);
}
}
catch (Exception)
{ }
}
private enum LogType
{ INFO, WARNING, ERROR }
}
Done...
I made everything in just one file for the sake of the example, you should divide things ... but what I was after was to show you the principle of it.
I don't know how you send them sms. But almost all big sms service providers will allow you to send 1000 within 1 seconds.
So Unless you REALLY REALLY REALLY REALLY REALLY REALLY need to be send them all at once, I suggest you just make a loop and send the information to the service provider one at the time.
Depends on how the SMS are actually sent. If you have let's say a web service that sends the SMS you'll end up in querying it 1000 times at one point which won't solve your problem.
To achieve this you need to make sure that the task of sending can be done simultaniously.
EDIT:
Furthermore I agree to astander that that amount of threads won't be healthy for your system at all.
Edit2:
How sharp does this needs to be? Assumeing that hh:mm is enough you'd have 60s to send
your about 1000 sms. This means that you need to send aprox 17 SMS per second. If you share
this to lets say 4 threads then you'd only need to make sure that your sending process /
device can send 4 SMS / s. this should be achievable I guess.
HTH
I dont think that is going to work for you, and creating such a large number of threads is not advised.
Also, see this link
maximum-number-of-threads-in-a-net-app
Does the SMS application allow for send-to-many? Or maybe use different services on various boxes to send these subset of sms. But i think sending such a volume at once will be difficult.
I suspect you'll have some transport issues getting that much data to your SMS provider at exactly that time presuming it is a realtime process.
I'd find a provider capable of doing scheduled sends and then queue up the messages to send at 12AM at my leisure.