Thursday, October 1, 2009

Problem inserting large characters strings in Oracle using NHibernate

I am currently on a very large eCommerce project at the State of VA and we came across a very interesting error from Oracle using NHibernate 1.2. For about 3 days, my team have been banging our heads against the wall to find out how to insert large character string of over 4000 characters in Oracle using NHibernate. We tried changing the column type in the database, adding in conventions to Fluent, changing proptery mappings, and we were even disperate enough to add multiple columns in to chuck the characters out in the database table.

Then I remember running across another blog post of a different issue where I saw a posting talking about Microsoft's version of  Oracle's data client does not handle large characters type to well where Oracle would push back a error message like this "ORA-01461: can bind a LONG value only for insert into a LONG column". Even though it was a clob column, got to love Oracle's clear and to the point error messages.

After a few minutes of changing around the web.config and NHibernate.config files, the insert worked! Below I will describe how to fix the problem.

Update NHibernate.config file
We were using the NHibernate.Driver.OracleClientDriver which default to Microsoft's Oracle provider (System.Data.OracleClient). The first chnage is to change the driver_class property to NHibernate.Driver.OracleDataClientDriver shown in figure 1.

Figure 1:
<property name="connection.driver_class">NHibernate.Driver.OracleDataClientDriver</property>

Add Reference to Oracle.DataAccess
Next, we have to add in the reference to Oracle.DataAccess dll to our website. This will allow NHibernate to find the dll to match the dialect. You can find this assembly in the oracle folder under ODP.NET.

Update Web.Config in your website
Now, we have to update our assemblybindings to accept the change for the addtion of Oracle.DataAccess dll. Below is the config change that needs to happen in your web.config.

figure 2:
<assemblybinding xmlns="urn:schemas-microsoft-com:asm.v1">
<qualifyassembly partialName="Oracle.DataAccess" fullName="Oracle.DataAccess, Version=2.102.2.20, Culture=neutral, PublicKeyToken=89b483f429c47342"/>
</assemblyBinding>

Happy Coding!

Tuesday, September 29, 2009

Handling Exceptions when Sending Mail

I am sure alot of us when sending mail, if the mail sent from your application didn’t go through; we just throw the exception and that's it. If we look into this alittle closer, the majority of mail server interruptions are very temporary, only lasting a few seconds. Instead of throwing the exception right away, why not send the mail again?



If there is a problem of any kind at the SMTP server, the server reports an exception, you will want to handle this in a SmtpException. While using the SmtpException works well, its functionality is limited in determining exactly how the send failed.

Why do we care? Well, sometimes our mail server is experiencing a serious issue, and most likely, there’s nothing you can do about it. But, when a SMTP server reports an error, the actual problem is not with the server itself but with an individual mailbox. The most common cause of mailbox issues are as follows:

  1. The destination mailbox is currently in use. if this occurs, only lasts a short amount of time.
  2. The mailbox is unavailable. This may mean that there is actually no such address on the server, but it may also mean that the search for the mailbox simply timed out.
  3. The transaction with the mailbox failed. The reasons for this can be somewhat mysterious; but like a stubborn web page, sometimes a second nudge is all it takes to do the trick.

What does this tell us? With a little more coding, we could gracefully recover from the majority of email send failures by detecting these particular conditions and attempting the send a second time.

Remember, if a second mail send happens to fail, then it’s very likely that the problem is not temporary, in which case there is not reason continuing to handle the exception at this point. When that happens, we can allow the exception to bubble up to the calling page as it normally would.


Handling the Exceptions

Problems with mailboxes is that they are not easily discovered by catching SmtpException. Fortunately, there is a more derived type called SmtpFailedRecipientException that the .NET Framework uses to wrap errors reported from an individual mailbox. This exception contains a StatusCode property of type enum that will tell us the exact cause of the error.

Obvserve in the following code below:

using System.Net.Mail;
using System.Threading;
using System.Web.Configuration;

/// <summary>
/// Provides a method for sending email.
/// </summary>
public static class Email
{
/// <summary>
/// Constructs and sends an email message.
/// </summary>
/// <param name="fromName">The display name of the person the email is from.</param>
/// <param name="fromEmail">The email address of the person the email is from.</param>
/// <param name="subject">The subject of the email.</param>
/// <param name="body">The body of the email.</param>
public static void Send(string fromName, string fromEmail, string subject, string body)
{
MailMessage message = new MailMessage
{
IsBodyHtml = false,
From = new MailAddress(fromEmail, fromName),
Subject = subject,
Body = body
};
message.To.Add(WebConfigurationManager.AppSettings["mailToAddress"]);

Send(message);
}

private static void Send(MailMessage message)
{
SmtpClient client = new SmtpClient();

try
{
client.Send(message);
}
catch (SmtpFailedRecipientException ex)
{
SmtpStatusCode statusCode = ex.StatusCode;

if (statusCode == SmtpStatusCode.MailboxBusy
statusCode == SmtpStatusCode.MailboxUnavailable
statusCode == SmtpStatusCode.TransactionFailed)
{
// wait 5 seconds, try a second time
Thread.Sleep(5000);
client.Send(message);
}
else
{
throw;
}
}
finally
{
message.Dispose();
}
}
}



As you can see in the above code block, we’re catching the SmtpFailedRecipientException that is thrown as a result of the mailbox error, and examining its StatusCode before actually handling the exception. If the reported error is due to the mailbox being busy or unavailable, or the transaction fails, we are using the Thread.Sleep(5000) moethod to wait five seconds before trying the send the mail message again. If the error is caused by any other reason, the exception is passed unhandled back to the caller, and subsequently back to the page, where it will be handled as its base type. A failure on the second send will also cause the exception to propagate back to the page.

Hope this helps... Happy Coding!