Introduction
I had this problem at work; I needed to send a notification via Lotus Notes Email with a description of the error (ex.Message
) and the name of the file that contained the error. Example: looking up values in a database that are not supplied in an XML file that I use to create a pipe delimited flat file going to another system. This would cause the flat file to not be processed; it would instead be moved to an error folder, and an event log entry would be created. Of course, this doesn't help the users, but an email would. The Internet doesn't have much in the way of C#-To-Lotus Notes, so I took some VBA samples, a few C# GUI samples, and modified them for my own needs, since what was there doesn't work for use in a Windows service. This gives the service the use of an application, and best of all, I don't have to do anything but check my email.
I also use this procedure to create an email to notify another user that a file was successfully processed and is awaiting them to import into the ERP system.
Background
What this does:
- Automates the email process from within a Windows service.
- Gives the user a brief description in the subject line. E.g.: Billing Error: File Not Processed.
- In the body of the email, it gives the user the file name, the error in understandable terms, and a useful tip on how to fix the problem. Case in point, some of our work orders do not have a lead tech assigned; this value is looked up in the database and is not contained in the XML file. The lack of a lead tech will cause an error to be thrown (I created several custom exceptions for this purpose, e.g.:
LeadTechNotFoundException
). I use this to tell the user to go into the database, add a lead tech, and then move the XML file from the error folder back into the processing folder. The service picks it up via a FileSystemWatchers
and re-processes it. If they copy instead of move, I check for the file and delete it if it exists. Simple, eh?
Using the code
Set your reference to the Domino COM Object.
This code can be used as is or modified. It's fully functional (at least on my systems).
static void SendNotesErrorMail( string err, string file)
{
string filename = file;
string errMessage = err;
NotesSession _notesSession = new NotesSession();
NotesDatabase _notesDataBase = null;
NotesDocument _notesDocument = null;
string sServerName = ConfigurationManager.AppSettings [ "ServerName" ];
string sMailFile = ConfigurationManager.AppSettings [ "MailFile" ];
string password = ConfigurationManager.AppSettings [ "Password" ];
string sSendTo = ConfigurationManager.AppSettings [ "SendTo" ];
string sSubject = "Billing Error";
object oItemValue = null;
string[] sCopyTo = new string[4];
sCopyTo [ 0 ] =
ConfigurationManager.AppSettings [ "Recipient0" ];
sCopyTo [ 1 ] =
ConfigurationManager.AppSettings [ "Recipient1" ];
sCopyTo [ 2 ] =
ConfigurationManager.AppSettings [ "Recipient2" ];
sCopyTo [ 3 ] =
ConfigurationManager.AppSettings [ "Recipient3" ];
_notesSession.Initialize(password);
_notesDataBase = _notesSession.GetDatabase(sServerName, sMailFile,
false);
if ( !_notesDataBase.IsOpen )
{
_notesDataBase.Open( );
}
_notesDocument = _notesDataBase.CreateDocument();
_notesDocument.ReplaceItemValue(
"Form", "Memo");
_notesDocument.ReplaceItemValue(
"SendTo", sSendTo);
_notesDocument.ReplaceItemValue(
"CopyTo", sCopyTo);
_notesDocument.ReplaceItemValue(
"Subject", sSubject);
NotesRichTextItem _richTextItem = _notesDocument.CreateRichTextItem("Body");
_richTextItem.AppendText(
"Error: " + errMessage + "\r\n");
_richTextItem.AppendText(
"File: " + filename + "\r\n");
_richTextItem.AppendText(
"Resolution: " + resolution + "\r\n");
oItemValue = _notesDocument.GetItemValue(
"SendTo" );
_notesDocument.Send(
false, ref oItemValue);
_richTextItem =
null;
_notesDocument =
null;
_notesDataBase =
null;
_notesSession =
null;
}
Points of Interest
This was a pain. There are a lot of VBA / Access / Excel with Lotus Notes how-to's etc. There is also a pretty nice example here from another user, with a GUI.
The changes I will make is to check a value in the app.config file and set the array size from that count. I also need to add some additional sections to the "resolution" section for my users. I may post a sample project, if I can get some time.
This is my first post here, so I hope it helps someone out.
Updates
- Writes the file errors to sql server for tracking purposes, captures the filename, datetime, error and so on.
- It creates and sends an email on a per file error/success basis.
- It sends a copy of all the email to the "primary" email address. There is a record of every file error and reason along with every report/workorder sent to a customer(s).
- I converted a manual process I used for testing into a windows service, which uses a directorymonitor to catch newly created files dropped into a folder and processes them on a FIFO basis.
- I left my errors commented out so you can see what was correct and what wasn't (at least for my usage)
- I use the
Configuration.Manager.AppSettings
variables b/c it's easier to stop a service, change a line than it is to edit code, recompile, test and deploy. This way you just stop the service, change the app.config file, save it and start the service. Works very well when the network guys change mail servers on you.
- At the very end is a sample of the App.Config file referenced in the code, for those of you who don't know how to use it.
Add the using System.net.mail
to the header section of your class.
public static void CreateSuccessMessage()
{
string mailServer = ConfigurationManager.AppSettings["ServerName"];
string SendTo = ConfigurationManager.AppSettings["BaanSendTo"];
string SendFrom = ConfigurationManager.AppSettings["SendFrom"];
MailMessage message = new MailMessage(SendFrom, SendTo);
message.Subject = "WorkOrder/Contract Import File Ready.";
message.Body = "File Ready To Process";
SmtpClient client = new SmtpClient(mailServer);
client.UseDefaultCredentials = false;
client.Send(message);
}
public static void SendSmtpMessage(int messageType, string workContract, string err, string fileName)
{
string mailServer = ConfigurationManager.AppSettings["ServerName"];
string SendFrom = ConfigurationManager.AppSettings["SendFrom"];
MailMessage message = new MailMessage();
if (messageType == 0)
{
}
else
{
string strSQL = "pS1FxXmlOutErrors_Insert ";
strSQL = strSQL + "'" + DateTime.Now + "'," + "'" + err + "'," + "'" + fileName + "'," + "'" + System.Environment.MachineName + "'," + "'" + workContract + "'";
string sqlConnectionString = ConfigurationManager.ConnectionStrings["AppConfigConnectionNameHere"].ConnectionString;
SqlConnection db_conn = new SqlConnection(sqlConnectionString);
SqlCommand ErrorInsertSqlCommand = new SqlCommand();
Trace.WriteLine(strSQL);
ErrorInsertSqlCommand.Connection = db_conn;
ErrorInsertSqlCommand.CommandText = strSQL;
ErrorInsertSqlCommand.CommandType = CommandType.Text;
db_conn.Open();
ErrorInsertSqlCommand.ExecuteNonQuery();
db_conn.Close();
string SendTo = ConfigurationManager.AppSettings["SendTo"];
string CopyTo = ConfigurationManager.AppSettings["Recipient1"];
message = new MailMessage(SendFrom, SendTo + "," + CopyTo);
message.Subject = System.Environment.MachineName + ": WorkOrder/Contract File Error.";
message.Body = "Originating Server: " + System.Environment.MachineName;
message.Body += System.Environment.NewLine;
message.Body += "WorkOrder/Contract Number: " + workContract;
message.Body += System.Environment.NewLine;
message.Body += "File That Caused The Error: " + fileName;
message.Body += System.Environment.NewLine;
message.Body += "The Error Message is: " + err;
message.Body += System.Environment.NewLine;
message.Body += "Suggested Action: Go Into System, Correct Values, Save And Re-Export";
}
SmtpClient client = new SmtpClient(mailServer);
client.UseDefaultCredentials = false;
client.Send(message);
}
public static void SendPdfWorkOrderCompletedEmail(string workContract, string ReportFileName, string strEmailTo, string strMailFrom)
{
string mailServer = ConfigurationManager.AppSettings["ServerName"];
string SendFrom = strMailFrom;
string SendTo = strEmailTo.Replace(";", ",");
SendTo = SendTo + ", hardcodedemainaddresshere";
string recptnts = SendTo.Replace(".com", "");
recptnts = recptnts.Replace(".net", "");
recptnts = recptnts.Replace(".org", "");
recptnts = recptnts.Replace(".biz", "");
MailMessage message = new MailMessage();
message = new MailMessage(SendFrom, SendTo);
message.Subject = "SourceOne Workorder " + workContract + " completed.";
message.Body = "To " + recptnts + ":";
message.Body += System.Environment.NewLine;
message.Body += System.Environment.NewLine;
message.Body += " Your e-mail address(es) are listed on this service event to receive an electronic copy of the Workorder.";
message.Body += System.Environment.NewLine;
message.Body += "The details of this event are included in the attached PDF file.";
message.Body += System.Environment.NewLine;
message.Body += "If you have received this e-mail in error, please reply to have your address removed from future Workorders.";
message.Body += System.Environment.NewLine;
message.Body += "Thank you for your Business.";
message.Body += System.Environment.NewLine;
message.Body += System.Environment.NewLine;
message.Attachments.Add(new Attachment(ReportFileName));
SmtpClient client = new SmtpClient(mailServer);
client.UseDefaultCredentials = false;
client.Send(message);
string strSQL = "StoredProcedureNameToUpdateCustomBoolField_pdfEmailSent ";
strSQL = strSQL + "'" + workContract + "'";
string sqlConnectionString = ConfigurationManager.ConnectionStrings["NameHere"].ConnectionString;
SqlConnection db_conn = new SqlConnection(sqlConnectionString);
SqlCommand PdfSentUpdateSqlCommand = new SqlCommand();
Trace.WriteLine(strSQL);
PdfSentUpdateSqlCommand.Connection = db_conn;
PdfSentUpdateSqlCommand.CommandText = strSQL;
PdfSentUpdateSqlCommand.CommandType = CommandType.Text;
db_conn.Open();
PdfSentUpdateSqlCommand.ExecuteNonQuery();
db_conn.Close();
}
public static void SendSmtpBillingFileIssuesMessage()
{
string mailServer = ConfigurationManager.AppSettings["ServerName"];
string SendFrom = ConfigurationManager.AppSettings["SendFrom"];
string SendTo = ConfigurationManager.AppSettings["Recipient5"];
MailMessage message = new MailMessage(SendFrom, SendTo);
message.Subject = "Flat Files To ERP System Building Up.";
message.Body = "Check that the File Transfer Service Is Running, If Not Start It.";
message.Body += System.Environment.NewLine;
message.Body += "Service Runs Every " + ConfigurationManager.AppSettings["ElapsedTime"];
message.Body += System.Environment.NewLine;
message.Body += "Check it again after that time frame, or wait for another mail message.";
SmtpClient client = new SmtpClient(mailServer);
client.UseDefaultCredentials = false;
client.Send(message);
}
AppConfig example: in the first set of quotes is the variable refd
in code, in the second set is the value.
<maillistadd key="FileInPath" value="C:\Folder\subFolder" />
<!-- The File Transfer Timer In Minutes-->
<add key="ElapsedTime" value="5"/>
<add key="ServerName" value="mail.somedomain.com"/>
<add key="SendTo" value="CommaSeperatedInHouseErrorlist"/>
<add key="SendFrom" value="mainemail@somedomain.com"/>
<add key="2nd email SendTo type" value="emaillist"/>
<add key="Recipient0" value="emaillist"/>
<add key="Recipient1" value="maillist"/>
<add key="Recipient2" value="maillist"/>
<add key="Recipient3" value="maillist"/>
<add key="Recipient4" value="maillist"/>
<add key="Recipient5" value="maillist"/>
</appSettings>
<connectionStrings>
<add name="ConnectionVariableName" connectionString="Data Source=ServerName;Initial Catalog=databaseName;Persist Security Info=True;User ID=UserID;Password=PW;MultipleActiveResultSets=True;"/>
</connectionStrings>
There you have it. Works very very very well for sending emails to customers, errors to internal departments, notifications to internal departments, and logging errors and issues in a datatable and reporting it.
Hope it helps.
Have a great day!