Hello people,
Good afternoon!
All right with you ?
In this post I will demonstrate how to send emails through the database using CLR (C #). In the post SQL Server - How to enable and configure Database mail to send and monitor database emails (sp_send_dbmail) I had already demonstrated how to send emails through SQL Server Database Mail, and this time I will show you how to do this directly from C #, without even having to configure anything in SQL Server to send the email.
Simple Email Sending
In this first example, I will demonstrate the simplest way to send an email in HTML format, with attachments, through the CLR using the C # programming language. See how easy it is:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 |
using System.Data.SqlTypes; using System.Net; using System.Net.Mail; public partial class StoredProcedures { [Microsoft.SqlServer.Server.SqlProcedure] public static void stpEnvia_Email(SqlString destinatarios, SqlString assunto, SqlString mensagem, SqlString arquivos) { const string smtpEndereco = "smtp.mail.yahoo.com"; const int smtpPorta = 587; const int smtpTimeout = 60000; // 60 segundos const bool smtpUsaCredenciaisPadrao = false; const bool smtpUsaSsl = true; const string smtpSenha = "senha"; using (var clienteSmtp = new SmtpClient(smtpEndereco, smtpPorta) {DeliveryMethod = SmtpDeliveryMethod.Network, Timeout = smtpTimeout, UseDefaultCredentials = smtpUsaCredenciaisPadrao, EnableSsl = smtpUsaSsl }) { if (!string.IsNullOrEmpty(smtpUsuario)) clienteSmtp.Credentials = new NetworkCredential(smtpUsuario, smtpSenha); using (var eMail = new MailMessage()) { var emailOrigem = new MailAddress(smtpUsuario); eMail.From = emailOrigem; foreach (var destinatario in destinatarios.Value.Split(';')) { if (!string.IsNullOrEmpty(destinatario)) eMail.To.Add(destinatario); } foreach (var arquivo in arquivos.Value.Split(';')) { if (!string.IsNullOrEmpty(arquivo)) eMail.Attachments.Add(new Attachment(arquivo)); } eMail.Subject = assunto.Value; eMail.IsBodyHtml = true; eMail.Body = (string.IsNullOrEmpty(mensagem.Value)) ? "" : mensagem.Value; clienteSmtp.Send(eMail); } } } } |
More complete email sending
Already in this second source code, I will demonstrate a more complete and organized way to create an Email Stored Procedure in CLR. To do this, I will separate the authentication information into separate classes and add the following additional features:
- Input Parameter Validation
- E-mail audit log recording
- RETRY routine to try to send email to 10x in case of failure
- Try..Catch for Error and Exception Handling
Prerequisites
Before demonstrating the source code for the email sending Stored Procedure, you will need to create the dependent classes below, in addition to the Return class, which I use to send error messages to the database. The code of this class you enter the post SQL Server - How to send warnings and error messages to the bank through CLR (C #).
Server.cs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 |
using System; using System.Collections.Generic; using System.Data.SqlClient; namespace Bibliotecas.Model { public class ServidorAtual { public string NomeServidor { get; set; } public ServidorAtual() { try { using (var conn = new SqlConnection(Servidor.Context)) { conn.Open(); using (var cmd = conn.CreateCommand()) { cmd.CommandText = "SELECT @@SERVERNAME AS InstanceName"; NomeServidor = (string) cmd.ExecuteScalar(); } var partes = NomeServidor.Split('\\'); if (partes.Length <= 1) return; if (string.Equals(partes[0], partes[1], StringComparison.CurrentCultureIgnoreCase)) NomeServidor = partes[0]; } } catch (Exception ex) { throw ex; } } public static string[] UsuarioAtual() { try { using (var conn = new SqlConnection(Servidor.Context)) { conn.Open(); using (var cmd = conn.CreateCommand()) { cmd.CommandText = "SELECT suser_name() AS usuario, host_name() AS maquina, APP_NAME() AS programa"; using (var dr = cmd.ExecuteReader()) { dr.Read(); var item = new string[3]; item.SetValue(dr["usuario"].ToString(), 0); item.SetValue(dr["maquina"].ToString(), 1); item.SetValue(dr["programa"].ToString(), 2); return item; } } } } catch (Exception ex) { throw ex; } } } public static class Servidor { public static string Ds_Servidor_SMTP => "smtp.mail.yahoo.com"; public static int Nr_Porta_SMTP => 587; public static bool Fl_Conexao_SSL => true; public static bool Fl_Credencial_Padrao_SMTP => false; public static int Nr_Timeout_SMTP => 60000; public static string Ds_Senha_SMTP => "senha"; public static string Ds_Usuario => "Usuario_CLR_Banco"; public static string Ds_Senha => "Senha_CLR_Banco"; public static string Context => "context connection=true"; public static string Localhost => "data source=LOCALHOST;initial catalog=CLR;Application Name=SQLCLR;persist security info=False;Enlist=False;packet size=4096;user id='" + Ds_Usuario + "';password='" + Ds_Senha + "'"; public static string getLocalhost() { var servidorAtual = new ServidorAtual().NomeServidor; return "data source=" + servidorAtual + ";initial catalog=CLR;Application Name=SQLCLR;persist security info=False;Enlist=False;packet size=4096;user id='" + Ds_Usuario + "';password='" + Ds_Senha + "'"; } public static List<string> Servidores { get { var servidores = new List<string> { Localhost }; return servidores; } } } } |
Utils.cs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 |
using System; using System.Data; using System.Data.SqlClient; using System.Data.SqlTypes; namespace Bibliotecas.Model { public static class Utils { public static void verificaParametroVazio(object input, string nomeInput, bool permiteStringVazia = false) { var msgNull = $"O valor do parâmetro '@{nomeInput}' não pode ser NULL"; var msgVazio = $"O valor do parâmetro '@{nomeInput}' não pode ser uma string vazia"; if (input is SqlString) { var parametro = (SqlString) input; if (parametro.IsNull) Retorno.Erro(msgNull); if (!permiteStringVazia && parametro.Value.Trim().Length == 0) Retorno.Erro(msgVazio); } else if (input is SqlChars) { var parametro = (SqlChars) input; if (parametro.IsNull) Retorno.Erro(msgNull); if (!permiteStringVazia && parametro.Value.Length == 0) Retorno.Erro(msgVazio); } else if (input is SqlInt16) { var parametro = (SqlInt16) input; if (parametro.IsNull) Retorno.Erro(msgNull); } else if (input is SqlInt32) { var parametro = (SqlInt32) input; if (parametro.IsNull) Retorno.Erro(msgNull); } else if (input is SqlInt64) { var parametro = (SqlInt64) input; if (parametro.IsNull) Retorno.Erro(msgNull); } else if (input is SqlBoolean) { var parametro = (SqlBoolean) input; if (parametro.IsNull) Retorno.Erro(msgNull); } else if (input is SqlByte) { var parametro = (SqlByte)input; if (parametro.IsNull) Retorno.Erro(msgNull); } else if (input is SqlBinary) { var parametro = (SqlBinary) input; if (parametro.IsNull) Retorno.Erro(msgNull); } else if (input is SqlDateTime) { var parametro = (SqlDateTime) input; if (parametro.IsNull) Retorno.Erro(msgNull); } else if (input is SqlDecimal) { var parametro = (SqlDecimal) input; if (parametro.IsNull) Retorno.Erro(msgNull); } else if (input is SqlDouble) { var parametro = (SqlDouble)input; if (parametro.IsNull) Retorno.Erro(msgNull); } else if (input is SqlGuid) { var parametro = (SqlGuid) input; if (parametro.IsNull) Retorno.Erro(msgNull); if (!permiteStringVazia && parametro.Value.ToString().Length == 0) Retorno.Erro(msgVazio); } else if (input is SqlXml) { var parametro = (SqlXml) input; if (parametro.IsNull) Retorno.Erro(msgNull); if (!permiteStringVazia && parametro.Value.Length == 0) Retorno.Erro(msgVazio); } else if (input is SqlMoney) { var parametro = (SqlMoney) input; if (parametro.IsNull) Retorno.Erro(msgNull); } else if (input is SqlSingle) { var parametro = (SqlSingle) input; if (parametro.IsNull) Retorno.Erro(msgNull); } } public static bool gravaLogEmail(SqlString destinatarios, SqlString assunto, SqlString mensagem, SqlString arquivos) { /* CREATE TABLE CLR.dbo.Log_Email ( Id_Log BIGINT IDENTITY(1, 1) NOT NULL, Dt_Log DATETIME DEFAULT GETDATE(), Ds_Destinatario VARCHAR(MAX) NOT NULL, Ds_Assunto VARCHAR(MAX) NULL, Ds_Mensagem VARCHAR(MAX) NULL, Ds_Arquivos VARCHAR(MAX) NULL, Ds_Usuario VARCHAR(100) NULL ) */ try { using (var conexao = new SqlConnection(Servidor.getLocalhost())) { conexao.Open(); using (var comando = new SqlCommand("INSERT INTO dbo.Log_Email (Ds_Destinatario, Ds_Assunto, Ds_Mensagem, Ds_Arquivos, Ds_Usuario) VALUES (@Ds_Destinatario, @Ds_Assunto, @Ds_Mensagem, @Ds_Arquivos, @Ds_Usuario)", conexao)) { var dadosUsuario = ServidorAtual.UsuarioAtual(); comando.Parameters.Add(new SqlParameter("@Ds_Destinatario", SqlDbType.VarChar, -1)).Value = destinatarios.Value; comando.Parameters.Add(new SqlParameter("@Ds_Assunto", SqlDbType.VarChar, -1)).Value = assunto.Value; comando.Parameters.Add(new SqlParameter("@Ds_Mensagem", SqlDbType.VarChar, -1)).Value = mensagem.Value; comando.Parameters.Add(new SqlParameter("@Ds_Arquivos", SqlDbType.VarChar, -1)).Value = arquivos.Value; comando.Parameters.Add(new SqlParameter("@Ds_Usuario", SqlDbType.VarChar, -1)).Value = dadosUsuario[0]; comando.ExecuteNonQuery(); return true; } } } catch (Exception e) { return false; } } } } |
Stored Procedure source code stpEnvia_Email:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 |
using System.Data.SqlTypes; using System.Linq; using System.Net; using System.Net.Mail; using System.Threading; using Bibliotecas.Model; public partial class StoredProcedures { [Microsoft.SqlServer.Server.SqlProcedure] public static void stpEnvia_Email(SqlString destinatarios, SqlString assunto, SqlString mensagem, SqlString arquivos) { Utils.verificaParametroVazio(destinatarios, nameof(destinatarios)); Utils.verificaParametroVazio(assunto, nameof(assunto)); Utils.verificaParametroVazio(mensagem, nameof(mensagem)); Utils.verificaParametroVazio(arquivos, nameof(arquivos), true); // Aceita string vazia using (var clienteSmtp = new SmtpClient(Servidor.Ds_Servidor_SMTP, Servidor.Nr_Porta_SMTP) {DeliveryMethod = SmtpDeliveryMethod.Network, Timeout = Servidor.Nr_Timeout_SMTP, UseDefaultCredentials = Servidor.Fl_Credencial_Padrao_SMTP, EnableSsl = Servidor.Fl_Conexao_SSL }) { if (!string.IsNullOrEmpty(Servidor.Ds_Usuario_SMTP)) clienteSmtp.Credentials = new NetworkCredential(Servidor.Ds_Usuario_SMTP, Servidor.Ds_Senha_SMTP); using (var eMail = new MailMessage()) { const int tentativas = 10; var sucesso = false; for (var i = 1; i <= tentativas; i++) { try { var emailOrigem = new MailAddress(Servidor.Ds_Remetente_SMTP); eMail.From = emailOrigem; foreach (var destinatario in destinatarios.Value.Split(';').Where(destinatario => !string.IsNullOrEmpty(destinatario))) eMail.To.Add(destinatario); foreach (var arquivo in arquivos.Value.Split(';').Where(arquivo => !string.IsNullOrEmpty(arquivo))) eMail.Attachments.Add(new Attachment(arquivo)); eMail.Subject = assunto.Value; eMail.IsBodyHtml = true; eMail.Body = (string.IsNullOrEmpty(mensagem.Value)) ? "" : mensagem.Value; clienteSmtp.Send(eMail); sucesso = true; } catch (SmtpException e) { if (e.StatusCode == SmtpStatusCode.ServiceClosingTransmissionChannel || e.StatusCode == SmtpStatusCode.TransactionFailed || e.StatusCode == SmtpStatusCode.GeneralFailure || e.StatusCode == SmtpStatusCode.ServiceNotAvailable) { if (i < tentativas) Retorno.Mensagem($"Erro na tentativa {i}: {e.Message}"); else Retorno.Erro($"Erro : {e.Message}\n\nInner Exception: {e.InnerException}"); } else Retorno.Erro($"Erro : {e.Message}\n\nStatus Code:{e.StatusCode}\nInner Exception: {e.InnerException}"); } if (sucesso) break; Thread.Sleep(10000); } } } Utils.gravaLogEmail(destinatarios, assunto, mensagem, arquivos); } } |
Examples of use
See below some examples of how to use this Stored Procedure in your daily life.
Simple Email Submission:
1 2 3 4 5 |
EXEC CLR.dbo.stpEnvia_Email @assunto = N'Assunto', -- nvarchar(max) @mensagem = N'Teste de Mensagem', -- nvarchar(max) @arquivos = N'' -- nvarchar(max) |
Sending email to multiple recipients:
1 2 3 4 5 |
EXEC CLR.dbo.stpEnvia_Email @assunto = N'Teste', -- nvarchar(max) @mensagem = N'Teste de Mensagem', -- nvarchar(max) @arquivos = N'' -- nvarchar(max) |
Sending email with attachment:
1 2 3 4 5 |
EXEC CLR.dbo.stpEnvia_Email @assunto = N'Assunto', -- nvarchar(max) @mensagem = N'Teste de Mensagem', -- nvarchar(max) @arquivos = N'C:\Windows\iis.log' -- nvarchar(max) |
Sending email with multiple attachments:
1 2 3 4 5 |
EXEC CLR.dbo.stpEnvia_Email @assunto = N'Assunto', -- nvarchar(max) @mensagem = N'Teste de Mensagem', -- nvarchar(max) @arquivos = N'C:\Windows\iis.log; C:\Windows\config.log; C:\Teste.txt' -- nvarchar(max) |
I hope you enjoyed the post and see you next time.
Hug!
Dirceu, how do I send email to multiple recipients, and will the email addresses be the result of a query?
Edvaldo, there is. Just create a while or cursor and send the email through variables
Anything, call me in the private
Dirceu, very good.
Missed the definition of Return Method, I took to test here and could not make it work
Leandro, you are right. I really forgot to put the Return class reference. I already updated the post by correcting this and the link to the Return class is here: https://www.dirceuresende.com/blog/sql-server-como-enviar-avisos-e-mensagens-de-erro-para-o-banco-pelo-clr-csharp/
Thank you for your help!
Very helpful Dirceu, thanks!
Very good Dirceu congratulations!