SSIS onError script task to send email - include exception details - c#

I have a SSIS package where I have created a Script task on the OnError event to send an email alerting users to the fact that an error has occurred.
This works fine but what I would like to do is include in my email body the exception message that caused the event handler to fire. How can I get access to the exception inside the script task?
Current script body:
/// <summary>
/// This method is called when this script task executes in the control flow.
/// Before returning from this method, set the value of Dts.TaskResult to indicate success or failure.
/// To open Help, press F1.
/// </summary>
public void Main()
{
try
{
string mailBody = "<p>XXXX package has failed.</p><p>Please investigate.</p>";
string mailFrom = Dts.Variables["MailFrom"].Value.ToString();
string errorMailTo = Dts.Variables["ErrorMailTo"].Value.ToString();
string smtpConnectionString = (string)(Dts.Connections["SMTPConnectionManager"].AcquireConnection(Dts.Transaction));
string smtpServer = smtpConnectionString.Split(new char[] { '=', ';' })[1];
var smtpClient = new SmtpClient(smtpServer);
var message = new MailMessage(mailFrom, errorMailTo, mailSubject, mailBody) { IsBodyHtml = true };
// TODO append exception message to the mail body.
smtpClient.Send(message);
Dts.TaskResult = (int)ScriptResults.Success;
}
catch (Exception ex)
{
string message = ex.Message;
if (ex.InnerException != null)
{
message = message + " " + ex.InnerException.Message;
}
Dts.Log("Error email sending failure - " + message, 0, new byte[0]);
Dts.TaskResult = (int)ScriptResults.Failure;
throw;
}
}

This is typically stored in the #[System::ErrorDescription] variable, which you will need to map as read only to access.
You can also use #[System::ErrorCode], but SSIS error codes are generally not very helpful.

Related

IOS Push Notification doesn't work using PushSharp

Im using PushSHarp on my project via Nuget and VisualStudio (c#) to try to send push notification through APN.
I created all of the file that required using my Apple account(development certificate, .p12 files etc).
Each time i used the right code i stil have the same error( i recreated all of the thing to be sure my token id is not bad):
ID= 1 Code= ConnectionError
PushSharp.Common.NotificationException: Invalid DeviceToken à
PushSharp.Apple.ApnsNotification.ToBytes() à
PushSharp.Apple.ApnsConnection.createBatch(List1 toSend) à
PushSharp.Apple.ApnsConnection.<SendBatch>d__21.MoveNext() Message
Apns notification error: 'ConnectionError' String
PushSharp.Apple.ApnsNotificationException: Apns notification error:
'ConnectionError' ---> PushSharp.Common.NotificationException: Invalid
DeviceToken à PushSharp.Apple.ApnsNotification.ToBytes() à
PushSharp.Apple.ApnsConnection.createBatch(List1 toSend) à
PushSharp.Apple.ApnsConnection.d__21.MoveNext()
Can you help me in this issue please? i really dont understand what happened.
I am using the below code given in their git wiki page to send the notifications here is the code
string TokenId = "f38205ce2137862735bd32e85b581dc85e2dc0abc04a2702e2899bddb9114543";
PushNotificationApple _notifservice = new PushNotificationApple();
_notifservice.SendAppleNotification(TokenId, "Test1");
public void SendAppleNotification(string tokenId, string message)
{
byte[] appleCert = null;
try
{
appleCert = File.ReadAllBytes("F:\\Dev\\ap.p12");
System.Diagnostics.Debug.WriteLine("OK LECTURE APPLE CERT");
}
catch (Exception ex)
{
var toto = ex.Message;
System.Diagnostics.Debug.WriteLine(" Exception lecture"+ ex.Message);
}
// Configuration (NOTE: .pfx can also be used here)
var config = new ApnsConfiguration(ApnsConfiguration.ApnsServerEnvironment.Sandbox, appleCert, "BrumbySolution99");
// Create a new broker
var apnsBroker = new ApnsServiceBroker(config);
// Wire up events
apnsBroker.OnNotificationFailed += (notification, aggregateEx) =>
{
aggregateEx.Handle(ex =>
{
// See what kind of exception it was to further diagnose
if (ex is ApnsNotificationException)
{
System.Diagnostics.Debug.WriteLine("ERRREUR NOTIFICATION");
var notificationException = (ApnsNotificationException)ex;
// Deal with the failed notification
var apnsNotification = notificationException.Notification;
var statusCode = notificationException.ErrorStatusCode;
System.Diagnostics.Debug.WriteLine("Apple Notification Failed: ID= " + notificationException.Notification.Identifier+" Code= "+ statusCode);
System.Diagnostics.Debug.WriteLine("link "+ ex.HelpLink);
System.Diagnostics.Debug.WriteLine("Inner "+ ex.InnerException);
System.Diagnostics.Debug.WriteLine("Message " + ex.Message);
System.Diagnostics.Debug.WriteLine("String " + ex.ToString());
}
else
{
// Inner exception might hold more useful information like an ApnsConnectionException
System.Diagnostics.Debug.WriteLine("Apple Notification Failed for some unknown reason : {ex.InnerException}");
}
// Mark it as handled
return true;
});
};
apnsBroker.OnNotificationSucceeded += (notification) =>
{
System.Diagnostics.Debug.WriteLine("APPLE NOTIFICATION SENT");
// Console.WriteLine("Apple Notification Sent!");
};
// Start the broker
apnsBroker.Start();
var notification_message = "{\"aps\":{\"alert\":{\"title\":\" Title " + message + "\",\"body\":\"Body " + message + "\"},\"badge\":\"1\"}}";
// Queue a notification to send
apnsBroker.QueueNotification(new ApnsNotification
{
DeviceToken = tokenId,
Payload = JObject.Parse(notification_message)
});
// Stop the broker, wait for it to finish
// This isn't done after every message, but after you're
// done with the broker
apnsBroker.Stop();
}
}

Constructing HTML table as Email Data in controller

I have C# code for sending Email (in Controller), that is working fine, Email gets shoot with no Problem
public ActionResult SendEmail(string msg, int formNum)
{
var sent = false;
try
{
var emailClient = new EmailServiceReference.EmailServiceClient();
sent = emailClient.SendEmail(fromEmail, toEmail, emailsubject+""+ formNum, msg);
// from and to values are defined.
}
catch (Exception ex)
{
Console.WriteLine("Exception occured while sending Email " + ex.Message);
}
return Json(sent, JsonRequestBehavior.AllowGet);
}
As you can see there are two parameters for SendEmail() method "msg" & "formNum". I am using those parameters to send the email as Subject(formNum) and Body(msg).
Now, I have 8- 10 parameters getting in SendEmail(FirstName, LastName, ID, Age, PhoneNumber, etc) Method and those I want to use in Email Body. The task for me is to create HTML table in Email body with all details and email it to XYZ#xyz.com
I need help to construct data in HTML table with data in Controller and how can I send it as argument (msg) in method call.?
Thank You
I have modified your code. See if this works.
using System.Net.Mail;
//...
public ActionResult SendEmail(string msg, int formNum)
{
var sent = false;
try
{
var emailClient = new SmtpClient();
var mailMessage = new MailMessage(fromEmail, toEmail);
mailMessage.Subject = emailsubject +" " + formNum;
mailMessage.Body = msg;
mailMessage.IsBodyHtml = true;
emailClient.Send(mailMessage);
sent = true;
// from and to values are defined.
}
catch (Exception ex)
{
Console.WriteLine("Exception occured while sending Email " + ex.Message);
}
return Json(sent, JsonRequestBehavior.AllowGet);
}

Getting Command Not Valid Error logging into Exchange Server using POP 3

I have an application that monitors an Exchange Server mailbox for incoming mail. It works on other systems, but for one of our customers we are getting an error: -ERR Command is not valid in this state.
I don't think it has anything to do with the code itself because we get the same error message when we try logging in using Telnet. The error comes when the User is passed. Just for reference, I have added my login code below.
try
{
tcpClient = new TcpClient(Host, Port);
}
catch (SocketException e) { ... }
String response = "";
try
{
streamReader = GetStreamReader(tcpClient);
response = streamReader.ReadLine();
if (response.StartsWith("+OK"))
{
response = SendReceive("USER ", UserName.Trim() + "#" + Domain.Trim());
if (response.StartsWith("+OK"))
response = SendReceive("PASS ", Password);
}
}
catch (Exception e) { ... }
And the SendReceive method is below:
private String SendReceive(String command, String parameter)
{
String result = null;
try
{
String myCommand = command.ToUpper().Trim() + " " + parameter.Trim() + Environment.NewLine;
byte[] data = System.Text.Encoding.ASCII.GetBytes(myCommand.ToCharArray());
tcpClient.GetStream().Write(data, 0, data.Length);
result = streamReader.ReadLine();
}
catch { } // Not logged in...
return result;
}
Some POP3 servers do not allow the USER command to be used until/unless the connection is using SSL.
In other words, you may need to use the STLS command first (if it is supported), or, failing that, you may be required to use a SASL authentication mechanism.
Check the results of the CAPA command for more information.
Oh, and shameless plug: use MailKit instead of trying to roll your own.

Fire and forget with async

Consider this code:
public async Task<Status> SendMessage(Message message)
{
List<IMessage> _messageDispatchers = new List<IMessage>();
try
{
Object[] args = new Object[] { _message };
IMessage endpoint = (IMessage)Activator.CreateInstance(Type.GetType(_message.AgentDLLName), args);
_messageDispatchers.Add(endpoint);
foreach (IMessage dispatcher in _messageDispatchers)
{
await Task.Run(() => dispatcher.SendMessage(_message));
}
return await Task.Run(() => Status.Success);
}
catch (Exception ex)
{
logger.Log(LoggerLevel.Error, ex.Message);
return Status.EmailSendingFailed;
}
}
the SendMessage:
public async Task<Status> SendMessage(OutboundMessage outboundmessage)
{
string strMessage = string.Empty;
string subject = string.Empty;
MessageServices objService = new MessageServices();
try
{
var config = (from SmtpConfigurationElement ms in AppConfiguration.Instance.Smtps
where ms.Key == "smtp"
select ms).Single();
SmtpClient smtpClient = new SmtpClient(config.Host);
smtpClient.Port = Convert.ToInt32(config.port);
smtpClient.EnableSsl = true;
smtpClient.Credentials = new NetworkCredential(config.UserName, config.Password);
string[] strToList = outboundmessage.ToList.Split(';');
MailMessage mail = new MailMessage();
mail.From = new MailAddress(outboundmessage.FromAddress);
if (strToList.Length > 0)
{
for (int j = 0; j < strToList.Length; j++)
{
mail.To.Add(strToList[j]);
}
}
else
{
_LOGGER.Log(LoggerLevel.Information, "SMTP Mail Send failed as ToList is not correct");
return Status.Failed;
}
if (!string.IsNullOrEmpty(outboundmessage.CCList))
{
string[] strCCList = outboundmessage.CCList.Split(';');
if (strCCList.Length > 0)
{
for (int k = 0; k < strCCList.Length; k++)
{
mail.CC.Add(strToList[k]);
}
}
}
if (!string.IsNullOrEmpty(outboundmessage.Attachments))
{
System.Net.Mail.Attachment attachment;
attachment = new System.Net.Mail.Attachment(outboundmessage.Attachments);
mail.Attachments.Add(attachment);
}
strMessage = await objService.ReplaceMessageWithPlaceholders(outboundmessage.PlaceholderValues, outboundmessage.MessageBody);
subject = await objService.ReplaceMessageWithPlaceholders(outboundmessage.PlaceholderValues, outboundmessage.Subject);
mail.Body = strMessage;
mail.Subject = subject;
mail.IsBodyHtml = true;
await Task.Run(() => smtpClient.Send(mail));
return Status.Success;
}
catch (Exception ex)
{
return Status.Failed;
}
}
And the call to SendMessage:
public Status MarketingEmail(OutboundMessage _message)
{
try
{
_message.MessageCreatedDate = System.DateTime.Now;
processor.SendMessage(_message);
return Status.Success;
}
catch (Exception ex)
{
_LOGGER.Log(LoggerLevel.Error, "Error in Marketing Email" + ex.ToString());
return Status.InsertFailed;
}
}
The whole idea is to make a workflow in which sending of the email is the last task and that should be a fire and forget thing.
Now the call to processor.SendMessage(_message) has a suggestion like this:
Because this call is not awaited, execution of the current method
continues before the call is completed. Consider applying the 'await'
operator to the result of the call.
Which is a valid thing since async & await need to be used together.
Questions:
Will the current approach work without any trouble if the suggestion is ignored?
(I am asking this since this is still in the development stage and I can make the suggested design changes now rather than face any critical issues later.)
What is the suggested best practice to design a workflow considering the said requirement?
The current approach will "work" in the sense that it will continue on to return Status.Success; without waiting for the call to processor.SendMessage(_message); to complete.
However, since that call was fired & forgotten, and that SendMessage overload doesn't do any logging in the catch block, you run the risk of emails failing to be sent but nobody getting notified about it.
A common approach for async email sending is this: Stash the email somewhere else (typically a message queue or a database), and then set up a separate async process that reads the queued emails and sends them. If it succeeds, it flags the email as sent. If it fails, it tries again (up to a certain time limit or # of retries), and then if it gives up, it can trigger a notification or set a flag that can be checked later.
Then your code will basically be saying "okay, the email was successfully queued", instead of "okay, the email was sent". Moving the actual sending to a separate process is much more reliable.

How do I trap Cancelling of a long-running deferred DTF Custom Action?

I've got a Deferred Custom Action DLL written in DTF that publishes a set of .RDL files to the SQL Server Reporting Web Service. All is working well and I can trap most of the error conditions in various Try Catch blocks.
The only thing I am having trouble with is if the user presses the Cancel button in the installer while the publish is happening. It does immediately pop up a message asking if I want to Cancel the install, but if I answer Yes then it throws a message :
Exception of type Microsoft.Deployment.WindowsInstaller.InstallCanceledException was thrown
and just an OK button.
I've tried adding a special Exception handler of
catch (InstallCanceledException ex)
{
}
prior to other exceptions, but it just doesn't seem to capture this one particular exception.
Any suggestions how to handle the InstallCanceledException during a Cancel of a long-running Deferred Custom Action?
The product team looked at using one of the applications but normal users run the applications and they wouldn't necessarily know the web service URL or have permissions to publish the reports to the web service. The installer I have put this in is usually used for running SQL Scripts and I'm adding a second Feature to the installer to Publish the reports. It's actually working too well to abandon it now. Product has seen what I've done already and they love it. The MSI Progress Bar is updating with the name of each report as they are published. The MSI prompts for the URI and user credentials and it already knows what folder the .RDL files are in. I run a Validation on the URI when they click the next button so by the time I run the Deferred action in the Execution Sequence it has a good URI and credentials. I've even gone so far as while the publish is occurring I disconnect from VPN and it fails with a proper error. It is literally only when the user presses Cancel that I can't seem to trap that one, but it is also not a showstopper for this work to go out.
Hiding the Cancel button is not an appropriate option since it is fine if they Cancel at any time.
public static ActionResult PublishSSRSReports(Session session)
{
session.Log("Begin PublishSSRSReports");
bool bFolderExists = false;
string sCustomActionData;
sCustomActionData = session["CustomActionData"];
string INSTALLDIR = Convert.ToString(MsiGetCustomActionDataAttribute(sCustomActionData, "/InstallDir="));
string SSRSURL = Convert.ToString(MsiGetCustomActionDataAttribute(sCustomActionData, "/SsrsUrl="));
string USERCREDENTIALS = Convert.ToString(MsiGetCustomActionDataAttribute(sCustomActionData, "/Credentials="));
string USERNAME = Convert.ToString(MsiGetCustomActionDataAttribute(sCustomActionData, "/Username="));
string PASSWORD = Convert.ToString(MsiGetCustomActionDataAttribute(sCustomActionData, "/Password="));
string ReportsFolderPath = INSTALLDIR + "SSRSReports";
DirectoryInfo directory = new DirectoryInfo(ReportsFolderPath);
FileInfo[] reports = directory.GetFiles("*.rdl"); //Getting all RDL files
ResetProgressBar(session, reports.Length);
CatalogItem[] catalogitem = null;
using (ReportingService2010 rsc = new ReportingService2010())
{
rsc.Url = SSRSURL;
if (USERCREDENTIALS == "0")
{
rsc.Credentials = System.Net.CredentialCache.DefaultCredentials; //User credential for Reporting Service
//the current logged system user
}
if (USERCREDENTIALS == "1")
{
string[] userdomain = USERNAME.Split(Convert.ToChar("\\"));
rsc.Credentials = new System.Net.NetworkCredential(userdomain[1], PASSWORD, userdomain[0]);
}
catalogitem = rsc.ListChildren(#"/", false);
foreach (CatalogItem catalog in catalogitem)
{
if (catalog.Name == (DP))
{
EventLog.WriteEntry(AppDomain.CurrentDomain.FriendlyName, DP + " folder already exists");
bFolderExists = true;
}
}
if (bFolderExists == false)
{
rsc.CreateFolder(DP, #"/", null);
}
Warning[] Warnings = null;
foreach (FileInfo ReportFile in reports)
{
Byte[] definition = null;
Warning[] warnings = null;
try
{
FileStream stream = ReportFile.OpenRead();
definition = new Byte[stream.Length];
stream.Read(definition, 0, (int)stream.Length);
stream.Close();
}
catch (InstallCanceledException ex)
{
//session.Message(InstallMessage.Error, new Record { FormatString = ex.Message });
EventLog.WriteEntry(AppDomain.CurrentDomain.FriendlyName, ex.Message);
return ActionResult.UserExit;
}
catch (IOException ex)
{
session.Message(InstallMessage.Error, new Record { FormatString = ex.Message });
EventLog.WriteEntry(AppDomain.CurrentDomain.FriendlyName, ex.Message);
return ActionResult.Failure;
}
catch (Exception ex)
{
session.Message(InstallMessage.Error, new Record { FormatString = ex.Message });
EventLog.WriteEntry(AppDomain.CurrentDomain.FriendlyName, ex.Message);
return ActionResult.Failure;
}
try
{
CatalogItem report = rsc.CreateCatalogItem("Report", ReportFile.Name, #"/" + DP, true, definition, null, out Warnings);
DisplayActionData(session, ReportFile.Name);
IncrementProgressBar(session, 1);
if (report != null)
{
EventLog.WriteEntry(AppDomain.CurrentDomain.FriendlyName, ReportFile.Name + " Published Successfully ");
}
if (warnings != null)
{
foreach (Warning warning in warnings)
{
EventLog.WriteEntry(AppDomain.CurrentDomain.FriendlyName, string.Format("Report: {0} has warnings", warning.Message));
}
}
else
{
EventLog.WriteEntry(AppDomain.CurrentDomain.FriendlyName, string.Format("Report: {0} created successfully with no warnings", ReportFile.Name));
}
}
catch (InstallCanceledException ex)
{
//session.Message(InstallMessage.Error, new Record { FormatString = ex.Message });
EventLog.WriteEntry(AppDomain.CurrentDomain.FriendlyName, ex.Message);
return ActionResult.UserExit;
}
catch (SoapException ex)
{
session.Message(InstallMessage.Error, new Record { FormatString = ex.Message });
EventLog.WriteEntry(AppDomain.CurrentDomain.FriendlyName, ex.Detail.InnerXml.ToString());
return ActionResult.Failure;
}
catch (Exception ex)
{
session.Message(InstallMessage.Error, new Record { FormatString = ex.Message });
EventLog.WriteEntry(AppDomain.CurrentDomain.FriendlyName, ex.Message);
return ActionResult.Failure;
}
}
}
return ActionResult.Success;
I've also got these in the class
private const string SpaceForwardSlash = " /";
private const string DP = "Test";
In the DTF source code the only place I see an InstallCanceledException being thrown is in Session.Message(). This is a wrapper for the MsiProcessMessage Windows API function. It looks to me like you would get this exception if you used Session.Message() to display a message box from a managed custom action, and then clicked the 'Cancel' button. DTF sees the message box 'cancel' return code and throws an InstallCanceledException. Perhaps it's then falling into a catch block somewhere (could be a different action?) where you call something similar to
session.Message(InstallMessage.Error, new Record { FormatString = ex.Message })
which displays the second message box containing just the exception.
I can't quite piece everything together 100% without seeing your MSI source or a complete log file, but maybe this will help.
Here's how Session.Message() is defined in the DTF source:
public MessageResult Message(InstallMessage messageType, Record record)
{
if (record == null)
{
throw new ArgumentNullException("record");
}
int ret = RemotableNativeMethods.MsiProcessMessage((int) this.Handle, (uint) messageType, (int) record.Handle);
if (ret < 0)
{
throw new InstallerException();
}
else if (ret == (int) MessageResult.Cancel)
{
throw new InstallCanceledException();
}
return (MessageResult) ret;
}

Categories