Posso configurar modelos de HTML / e-mail com ASP.NET?

97

Estou trabalhando em um site que enviará um número significativo de e-mails. Desejo configurar o texto do cabeçalho e do rodapé, ou talvez até modelos para permitir que os usuários editem facilmente esses e-mails, se necessário.

Se eu incorporar o HTML dentro de literais de string C #, ele ficará feio e eles terão que se preocupar em escapar. Incluir arquivos simples para o cabeçalho e rodapé pode funcionar, mas algo sobre isso simplesmente não parece certo.

O ideal seria usar uma .ASPXpágina como template de alguma forma, então apenas dizer ao meu código para servir aquela página, e usar o HTML retornado para o email.

Existe uma maneira fácil e agradável de fazer isso? Existe uma maneira melhor de resolver este problema?

Atualizado:
adicionei uma resposta que permite que você use uma página .aspx padrão como modelo de e-mail. Basta substituir todas as variáveis ​​como faria normalmente, usar ligação de dados, etc. Em seguida, apenas capture a saída da página e pronto! Você tem seu e-mail em HTML!

ATUALIZADO COM CAVEAT !!!:
Eu estava usando a classe MailDefinition em algumas páginas aspx perfeitamente, mas ao tentar usar essa classe durante um processo do servidor em execução, ela falhou. Acredito que foi porque o método MailDefinition.CreateMailMessage () requer um controle válido para fazer referência, embora nem sempre faça algo. Por causa disso, eu recomendaria minha abordagem usando uma página aspx, ou a abordagem de Mun usando uma página ascx, o que parece um pouco melhor.

John Bubriski
fonte
Outra solução seria usar o AlphaMail para criar e enviar seus emails usando C # e a linguagem de modelo Comlang .
Timothy E. Johansson
1
@JohnBubriski: trabalho eu em torno de problema de controle que você mencionou em "atualizado com CAVEAT" usando new System.Web.UI.Control()como em: mailDefinition.CreateMailMessage("[email protected]", iDictionaryReplacements, new System.Web.UI.Control()).
Theophilus
Sim, eu também fiz isso, mas com o advento do Razor, isso está se tornando uma ideia menos boa.
John Bubriski

Respostas:

73

Já há uma tonelada de respostas aqui, mas me deparei com um ótimo artigo sobre como usar o Razor com modelos de e-mail. O Razor foi implementado com ASP.NET MVC 3, mas o MVC não é necessário para usar o Razor. Este é um processamento muito bom de fazer modelos de e-mail

Como o artigo identifica, "A melhor coisa do Razor é que, ao contrário de seu antecessor (webforms), ele não está vinculado ao ambiente da web, podemos hospedá-lo facilmente fora da web e usá-lo como mecanismo de modelo para diversos fins."

Gerando e-mails HTML com RazorEngine - Parte 01 - Introdução

Aproveitando os modelos do Razor fora do ASP.NET: eles não são apenas para HTML!

Modelos de email mais inteligentes em ASP.NET com RazorEngine

Controle de qualidade Stackoverflow semelhante

Modelagem usando a nova API RazorEngine

Usando Razor sem MVC

É possível usar o Razor View Engine fora do asp.net

Mike Barlow - BarDev
fonte
1
1, mas tenha cuidado se os usuários estiverem fornecendo os modelos, pois eles podem executar o código C # a partir do modelo, dando a eles muito mais poder em seu sistema do que você provavelmente gostaria.
AaronLS
O que você acha sobre segurança. O uso desse mecanismo de modelagem torna possível formatar todo o sistema de arquivos. Gosto do motor, mas isso me obriga a dar uma olhada em outros motores.
der_chirurg
55

Você também pode tentar carregar um controle e, em seguida, renderizá-lo em uma string e defini-lo como o corpo do HTML:

// Declare stringbuilder to render control to
StringBuilder sb = new StringBuilder();

// Load the control
UserControl ctrl = (UserControl) LoadControl("~/Controls/UserControl.ascx");

// Do stuff with ctrl here

// Render the control into the stringbuilder
StringWriter sw = new StringWriter(sb);
Html32TextWriter htw = new Html32TextWriter(sw);
ctrl.RenderControl(htw);

// Get full body text
string body = sb.ToString();

Você pode então construir seu e-mail como de costume:

MailMessage message = new MailMessage();
message.From = new MailAddress("[email protected]", "from name");
message.Subject = "Email Subject";
message.Body = body;
message.BodyEncoding = Encoding.ASCII;
message.IsBodyHtml = true;

SmtpClient smtp = new SmtpClient("server");
smtp.Send(message);

Seu controle de usuário pode conter outros controles, como cabeçalho e rodapé, e também tirar proveito da funcionalidade, como vinculação de dados.

Mun
fonte
De alguma forma, perdi essa resposta da primeira vez ... boa. Semelhante à minha solução, mas com um ascx em vez de um aspx. Ainda acho que aspx seria melhor, já que ofereceria uma página completa, ao invés de um controle, mas é o que eu penso.
John Bubriski
Sim, você poderia usar qualquer uma das soluções ... Eles funcionam da mesma maneira. Um benefício dessa abordagem é a consistência. Por exemplo, você pode mostrar a um usuário um resumo do pedido e incluir exatamente a mesma coisa no e-mail de confirmação reutilizando o mesmo controle.
Mun
Ponto secundário, mas está faltando uma linha para declarar um StringBuilder no primeiro bloco de código.
Kirschstein,
9
O exemplo não explica onde o código reside, é uma página ?, porque LoadControl é um método de página / controle.
Shrage Smilowitz
@Mun, você carrega o usercontrol em uma variável chamada ctrl, e você nunca faz referência a ele novamente em seu código. Como é que isso deveria funcionar?
The Muffin Man
35

Você pode tentar a classe MailDefinition

John Sheehan
fonte
4
Só quero salientar que isso é bom para e-mails básicos, mas não para nada complexo. A classe MailDefinition não oferece suporte a vinculação de dados. A única coisa que realmente faz é oferecer substituições de cordas. Embora, também esteja embutido no Assistente de Criação de Conta de Associação.
John Bubriski
4
A classe MailDefinition deve obter um Control para renderizar o conteúdo modelado. Não é tão bom.
Yuki
17

Se você deseja passar parâmetros como nomes de usuários, nomes de produtos, ... etc., você pode usar o mecanismo de modelo de código aberto NVelocity para produzir seu e-mail / HTML final.

Um exemplo de modelo NVelocity ( MailTemplate.vm ):

A sample email template by <b>$name</b>.
<br />

Foreach example :
<br />    
#foreach ($item in $itemList)

[Date: $item.Date] Name: $item.Name, Value: $itemValue.Value
<br /><br />

#end

Gerando corpo de e-mail por MailTemplate.vm em seu aplicativo:

VelocityContext context = new VelocityContext();
context.Put("name", "ScarletGarden");
context.Put("itemList", itemList);

StringWriter writer = new StringWriter();

Velocity.MergeTemplate("MailTemplate.vm", context, writer);

string mailBody = writer.GetStringBuilder().ToString();

O corpo do email de resultado é:

Um modelo de email de amostra da ScarletGarden .

Por exemplo:

[Data: 12.02.2009] Nome: Item 1, Valor: 09

[Data: 21.02.2009] Nome: Item 4, Valor: 52

[Data: 01.03.2009] Nome: Item 2, Valor: 21

[Data: 23.03.2009] Nome: Item 6, Valor: 24

Para editar os modelos, talvez você possa usar o FCKEditor e salvar seus modelos em arquivos.

Canavar
fonte
7

O componente de e-mail Mail.dll inclui mecanismo de modelo de e-mail:

Esta é a visão geral da sintaxe:

<html>
<body>
Hi {FirstName} {LastName},

Here are your orders: 
{foreach Orders}
    Order '{Name}' sent to <strong>{Street}</strong>. 
{end}

</body>
</html>

E o código que carrega o modelo, preenche os dados do objeto c # e envia um e-mail:

Mail.Html(Template
              .FromFile("template.txt")
              .DataFrom(_contact)
              .Render())
    .Text("This is text version of the message.")
    .From(new MailBox("[email protected]", "Alice"))
    .To(new MailBox("[email protected]", "Bob"))
    .Subject("Your order")
    .UsingNewSmtp()
    .WithCredentials("[email protected]", "password")
    .Server("mail.com")
    .WithSSL()
    .Send();

Você pode obter mais informações na postagem do blog do mecanismo de modelo de email .

Ou apenas baixe o componente de e- mail Mail.dll e experimente.

Observe que este é um produto comercial que criei.

Pawel Lesnikowski
fonte
6

Se a flexibilidade for um dos seus pré-requisitos, o XSLT pode ser uma boa escolha, que é totalmente compatível com o .NET framework e você pode até mesmo permitir que o usuário edite esses arquivos. Este artigo ( http://www.aspfree.com/c/a/XML/XSL-Transformations-using-ASP-NET/ ) pode ser útil para começar (msdn tem mais informações sobre isso). Conforme dito por ScarletGarden, NVelocity é outra boa escolha, mas eu prefiro XSLT por seu suporte de framework .NET "integrado" e plataforma agnóstica.

Everton
fonte
Eu nunca tinha pensado nisso antes, mas depois de tentar muitos outros métodos, descobri que funcionava muito bem em combinação com a adição da IXmlSerializableinterface às minhas classes. Em apenas algumas linhas, posso fazer com que minha classe receba um e-mail.
cjbarth
Urgh, eu sonhei pesadelos com XSLT. Provavelmente a linguagem de programação / marcação mais não intuitiva com a qual já trabalhei. E impossível de manter para outros e até para você mesmo 1 mês depois de codificar seu XSLT pela primeira vez.
PussInBoots de
5

Acho que você também pode fazer algo assim:

Crie uma página .aspx e coloque-a no final do método OnLoad ou chame-a manualmente.

    StringBuilder sb = new StringBuilder();
    StringWriter sw = new StringWriter(sb);
    HtmlTextWriter htmlTW = new HtmlTextWriter(sw);
    this.Render(htmlTW);

Não tenho certeza se há algum problema potencial com isso, mas parece que funcionaria. Dessa forma, você pode usar uma página .aspx com todos os recursos, em vez da classe MailDefinition que só oferece suporte a substituições de texto.

John Bubriski
fonte
Embora a classe MailDefinition seja um bom começo, é um pouco rudimentar. Este método deve suportar muito mais recursos, como ligação de dados e talvez até rastreamento. Alguma ideia sobre isso ou possíveis pegadinhas?
John Bubriski
Ótimo! Você teve algum problema com isso?
John Bubriski
Então, você vai permitir que seus usuários editem os arquivos .aspx quando precisarem fazer alterações no modelo de email? Eu diria que isso é um problema potencial.
Bryan
Eu não penso assim, pelo menos, não é mais arriscado do que outros modelos que eles poderiam editar. Concedido, se eles soubessem o que estão fazendo, eles poderiam causar danos, mas neste caso, pelo menos, é improvável. Não seria uma página .aspx complexa, mais um modelo com espaços reservados.
John Bubriski
Já faz um tempo, eu sei, mas você se lembra da sua solução final? Não consegui fazer com que essa abordagem específica funcionasse com um Page, pelo menos ao usar um método de extensão genérico para a renderização. Assim, mudei para UserControl; veja minha resposta abaixo. Até agora, parece estar funcionando bem ... Gostaria de saber como você resolveu na época.
InteXX 01 de
4

Claro que você pode criar um modelo html e eu recomendaria também um modelo de texto. No template você pode simplesmente colocar [BODY] no lugar onde o corpo seria colocado e então você pode apenas ler o template e substituir o corpo pelo novo conteúdo. Você pode enviar o e-mail usando .Nets Mail Class. Você apenas tem que repetir o envio do e-mail para todos os destinatários após criar o e-mail inicialmente. Caiu como uma luva para mim.

using System.Net.Mail;

// Email content
string HTMLTemplatePath = @"path";
string TextTemplatePath = @"path";
string HTMLBody = "";
string TextBody = "";

HTMLBody = File.ReadAllText(HTMLTemplatePath);
TextBody = File.ReadAllText(TextTemplatePath);

HTMLBody = HTMLBody.Replace(["[BODY]", content);
TextBody = HTMLBody.Replace(["[BODY]", content);

// Create email code
MailMessage m = new MailMessage();

m.From = new MailAddress("[email protected]", "display name");
m.To.Add("[email protected]");
m.Subject = "subject";

AlternateView plain = AlternateView.CreateAlternateViewFromString(_EmailBody + text, new System.Net.Mime.ContentType("text/plain"));
AlternateView html = AlternateView.CreateAlternateViewFromString(_EmailBody + body, new System.Net.Mime.ContentType("text/html"));
mail.AlternateViews.Add(plain);
mail.AlternateViews.Add(html);

SmtpClient smtp = new SmtpClient("server");
smtp.Send(m);
Josh Mein
fonte
Você pode cortar o material do StreamReader e substituir por File.ReadAllText (path)
John Sheehan
Este é um bom começo, mas fornece funcionalidade apenas para cabeçalho e rodapé. Isso realmente não ajuda com o próprio corpo.
John Bubriski
O corpo tudo que você precisa fazer é inserir o conteúdo do corpo que é desejado nos campos HTMLBody e TextBody ou você também pode armazená-los em arquivos
Josh Mein
4

Aqui está mais uma alternativa que usa transformações XSL para modelos de email mais complexos: Envio de email com base em HTML de aplicativos .NET .

Alek Davis
fonte
2
Curta o link. Obrigado! Meu cérebro começou a girar e percebi que eu poderia dar um passo adiante e ter um modelo XSLT que leva um objeto XML serializável ou contrato de dados WCF direto para o formato de email html. De repente, eu teria modelos de e-mail 'fortes' por meio de classes serializáveis ​​reais!
CodingWithSpike
2

Tenha cuidado ao fazer isso, os filtros de SPAM parecem bloquear o html gerado pelo ASP.net, aparentemente por causa do ViewState, portanto, se você for fazer isso, certifique-se de que o HTML produzido está limpo.

Eu pessoalmente gostaria de usar o Asp.net MVC para alcançar os resultados desejados. ou NVelocity é muito bom nisso

danswain
fonte
1

O ideal seria usar uma página .ASPX como modelo de alguma forma, então apenas dizer ao meu código para servir essa página e usar o HTML retornado para o e-mail.

Você poderia facilmente construir um WebRequest para acessar uma página ASPX e obter o HTML resultante. Com um pouco mais de trabalho, você provavelmente conseguirá fazer isso sem o WebRequest. Um PageParser e um Response.Filter permitiriam que você execute a página e capture a saída ... embora possa haver algumas maneiras mais elegantes.

Mark Brackett
fonte
1

Eu tinha um requisito semelhante em um dos projetos em que era necessário enviar um grande número de e-mails todos os dias, e o cliente queria controle completo sobre os modelos de html para diferentes tipos de e-mail.

devido ao grande número de e-mails a serem enviados, o desempenho era a principal preocupação.

o que criamos foi o conteúdo estático no servidor sql, onde você salva a marcação inteira do modelo html (junto com espaços reservados, como [UserFirstName], [UserLastName] que são substituídos por dados reais em tempo de execução) para diferentes tipos de e-mails

em seguida, carregamos esses dados no cache do asp.net - portanto, não lemos os modelos html repetidamente - mas apenas quando eles são realmente alterados

demos ao cliente um editor WYSIWYG para modificar esses modelos por meio de um formulário da web de administrador. sempre que atualizações foram feitas, nós redefinimos o cache do asp.net.

e então tínhamos uma tabela separada para logs de email - onde cada email a ser enviado era registrado. esta tabela tinha campos chamados emailType, emailSent e numberOfTries.

nós simplesmente executamos um trabalho a cada 5 minutos para tipos de e-mail importantes (como inscrição de novo membro, senha esquecida) que precisam ser enviados o mais rápido possível

executamos outro trabalho a cada 15 minutos para tipos de e-mail menos importantes (como e-mail de promoção, e-mail de notícias, etc)

desta forma você não bloqueia seu servidor enviando emails sem parar e processa emails em lote. assim que um e-mail for enviado, você define o campo emailSent como 1.

Raj
fonte
Mas como você lidou com as coleções?
Riri de
1
Eu também fiz isso e funcionou bem. Além disso, você pode, historicamente, voltar e ver os registros de e-mails enviados, se você preferir relatórios.
Mark Glorie
1

Observe que as soluções aspx e ascx requerem um HttpContext atual, portanto, não podem ser usadas de forma assíncrona (por exemplo, em threads) sem muito trabalho.

Rosco
fonte
1

Acho que a resposta fácil é MvcMailer. É um pacote NuGet que permite usar seu mecanismo de visualização favorito para gerar e-mails. Veja o pacote NuGet aqui e a documentação do projeto

Espero que ajude!

Sohan
fonte
Hmm, estranho essa resposta não ter recebido muita atenção ?!
PussInBoots de
1

DotLiquid é outra opção. Você especifica os valores de um modelo de classe {{ user.name }}e, em seguida, no tempo de execução, fornece os dados dessa classe e o modelo com a marcação, e ele mesclará os valores para você. É semelhante ao uso do mecanismo de modelagem Razor de várias maneiras. Suporta coisas mais complexas como loops e várias funções como ToUpper. O bom é que eles são "seguros" para que os usuários que criam os modelos não possam travar seu sistema ou escrever códigos inseguros como você faria no razor: http://dotliquidmarkup.org/try-online

AaronLS
fonte
0

Se você é capaz de permitir que a permissão ASPNET e os usuários associados a ler e escrever um arquivo, você pode facilmente usar um arquivo HTML com padrão String.Format()espaços reservados ( {0}, {1:C}, etc.) para alcançar este objetivo.

Simplesmente leia no arquivo, como uma string, usando classes do System.IOnamespace. Depois de ter essa string, passe-a como o primeiro argumento para String.Format()e forneça os parâmetros.

Mantenha esse barbante por perto e use-o como o corpo do e-mail, e está basicamente pronto. Fazemos isso em dezenas de sites (reconhecidamente pequenos) hoje, e não tivemos problemas.

Devo observar que isso funciona melhor se (a) você não estiver enviando zilhões de e-mails de uma vez, (b) não estiver personalizando cada e-mail (caso contrário, você comerá uma tonelada de cordas) e (c ) o arquivo HTML em si é relativamente pequeno.

John Rudy
fonte
0

Defina o conjunto de mensagem de e-mail IsBodyHtml = true

Pegue o seu objeto que contém o conteúdo do seu e-mail. Serialize o objeto e use xml / xslt para gerar o conteúdo html.

Se você quiser fazer AlternateViews, faça a mesma coisa que jmein, use apenas um modelo xslt diferente para criar o conteúdo de texto simples.

uma das principais vantagens disso é se você quiser alterar seu layout, basta atualizar o modelo xslt.

Bob o zelador
fonte
0

Olhe para SubSonic (www.subsonicproject.com). Eles estão fazendo exatamente isso para gerar código - o modelo é ASPX padrão e produz c #. O mesmo método seria reutilizável para o seu cenário.

jvenema
fonte
0

Eu usaria uma biblioteca de modelos como TemplateMachine . isso permite que você coloque principalmente seu modelo de e-mail junto com o texto normal e, em seguida, use regras para injetar / substituir valores conforme necessário. Muito semelhante ao ERB em Ruby. Isso permite que você separe a geração do conteúdo do e-mail sem amarrá-lo muito a algo como ASPX, etc., então, uma vez que o conteúdo seja gerado com isso, você pode enviar o e-mail.

MikeJ
fonte
0

Gosto da resposta de Raj. Programas como ListManager e frameworks como DNN fazem coisas semelhantes, e se a edição fácil por usuários não técnicos for necessária, os editores WYSIWYG para modificar o HTML armazenado em SQL é uma maneira mais fácil e direta de usar e pode acomodar facilmente cabeçalhos de edição independentemente dos rodapés, etc, bem como usar tokens para inserir valores dinamicamente.

Uma coisa a ter em mente ao usar o método acima (ou qualquer outro, na verdade) é ser estrito e cuidadoso sobre quais tipos de estilo e tags você permite que os editores inseram. Se você acha que os navegadores são meticulosos, espere até ver como os clientes de e-mail processam a mesma coisa de maneira diferente ...

usuario
fonte
0

Semelhante à resposta de Canavar, mas em vez de NVelocity, eu sempre uso " StringTemplate ", que carrego o modelo de um arquivo de configuração, ou carrego um arquivo externo usando File.ReadAllText () e defino os valores.

É um projeto Java, mas a porta C # é sólida e eu a usei em vários projetos (apenas usei para modelagem de email usando o modelo em um arquivo externo).

As alternativas são sempre boas.

Bryan Bailliache
fonte
0

Esta é uma maneira simples de usar a classe WebClient :

public static string GetHTMLBody(string url)
{
    string htmlBody;

    using (WebClient client = new WebClient ())
    {
        htmlBody = client.DownloadString(url);
    }

    return htmlBody;
}

Então chame assim:

string url = "http://www.yourwebsite.com";
message.Body = GetHTMLBody(url);

Obviamente, seu CSS precisará estar alinhado para mostrar os estilos da página da Web na maioria dos clientes de e-mail (como o Outlook). Se o seu e-mail exibir conteúdo dinâmico (por exemplo, Nome do cliente), recomendo usar QueryStrings em seu site para preencher os dados. (por exemplo, http://www.yourwebsite.com?CustomerName=Bob )

ROFLwTIME
fonte
Legal, embora eu ache que a maioria das outras respostas faz isso sem fazer uma solicitação da web de volta ao site, ou seja, ter que hospedar o corpo do e-mail em seu site.
Rup
@Rup Compreensível, mas tenha em mente que muitas vezes as pessoas querem ver uma "versão da Web" do e-mail de qualquer maneira. Esta solução funciona perfeitamente para esse cenário.
ROFLwTIME
0

@bardev fornece uma boa solução, mas infelizmente não é ideal em todos os casos. O meu era um deles.

Estou usando WebForms em um site (juro que nunca mais usarei um site - que PITA) no VS 2013.

Tentei a sugestão do Razor, mas por ser um site, não recebi o importante IntelliSense que o IDE oferece em um projeto MVC. Também gosto de usar o designer para meus modelos - um local perfeito para um UserControl.

Nix em Razor novamente.

Então, eu vim com esta pequena estrutura em vez disso (dicas para @mun para UserControl e @imatoria para digitação forte). Praticamente o único ponto de problema potencial que posso ver é que você deve ter cuidado para manter o nome do arquivo .ASCX em sincronia com o nome da classe. Se você se desviar, receberá um erro de execução.

FWIW: Em meus testes, pelo menos a chamada RenderControl () não gosta de um controle de página, então optei por UserControl.

Tenho certeza de que incluí tudo aqui; me avise se eu omiti algo.

HTH

Uso:

Partial Class Purchase
  Inherits UserControl

  Private Sub SendReceipt()
    Dim oTemplate As MailTemplates.PurchaseReceipt

    oTemplate = MailTemplates.Templates.PurchaseReceipt(Me)
    oTemplate.Name = "James Bond"
    oTemplate.OrderTotal = 3500000
    oTemplate.OrderDescription = "Q-Stuff"
    oTemplate.InjectCss("PurchaseReceipt")

    Utils.SendMail("{0} <[email protected]>".ToFormat(oTemplate.Name), "Purchase Receipt", oTemplate.ToHtml)
  End Sub
End Class

Classe Base:

Namespace MailTemplates
  Public MustInherit Class BaseTemplate
    Inherits UserControl

    Public Shared Function GetTemplate(Caller As TemplateControl, Template As Type) As BaseTemplate
      Return Caller.LoadControl("~/MailTemplates/{0}.ascx".ToFormat(Template.Name))
    End Function



    Public Sub InjectCss(FileName As String)
      If Me.Styler IsNot Nothing Then
        Me.Styler.Controls.Add(New Controls.Styler(FileName))
      End If
    End Sub



    Private ReadOnly Property Styler As PlaceHolder
      Get
        If _Styler Is Nothing Then
          _Styler = Me.FindNestedControl(GetType(PlaceHolder))
        End If

        Return _Styler
      End Get
    End Property
    Private _Styler As PlaceHolder
  End Class
End Namespace

Classe "Fábrica":

Namespace MailTemplates
  Public Class Templates
    Public Shared ReadOnly Property PurchaseReceipt(Caller As TemplateControl) As PurchaseReceipt
      Get
        Return BaseTemplate.GetTemplate(Caller, GetType(PurchaseReceipt))
      End Get
    End Property
  End Class
End Namespace

Classe de modelo:

Namespace MailTemplates
  Public MustInherit Class PurchaseReceipt
    Inherits BaseTemplate

    Public MustOverride WriteOnly Property Name As String
    Public MustOverride WriteOnly Property OrderTotal As Decimal
    Public MustOverride WriteOnly Property OrderDescription As String
  End Class
End Namespace

Cabeçalho ASCX:

<%@ Control Language="VB" ClassName="_Header" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional //EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<!--
  See https://www.campaignmonitor.com/blog/post/3317/ for discussion of DocType in HTML Email
-->

<html xmlns="http://www.w3.org/1999/xhtml">
<head>
  <title></title>
  <asp:PlaceHolder ID="plcStyler" runat="server"></asp:PlaceHolder>
</head>
<body>

Rodapé ASCX:

<%@ Control Language="VB" ClassName="_Footer" %>

</body>
</html>

Modelo ASCX:

<%@ Control Language="VB" AutoEventWireup="false" CodeFile="PurchaseReceipt.ascx.vb" Inherits="PurchaseReceipt" %>

<%@ Register Src="_Header.ascx" TagName="Header" TagPrefix="uc" %>
<%@ Register Src="_Footer.ascx" TagName="Footer" TagPrefix="uc" %>

<uc:Header ID="ctlHeader" runat="server" />

  <p>Name: <asp:Label ID="lblName" runat="server"></asp:Label></p>
  <p>Order Total: <asp:Label ID="lblOrderTotal" runat="server"></asp:Label></p>
  <p>Order Description: <asp:Label ID="lblOrderDescription" runat="server"></asp:Label></p>

<uc:Footer ID="ctlFooter" runat="server" />

Arquivo de código do modelo ASCX:

Partial Class PurchaseReceipt
  Inherits MailTemplates.PurchaseReceipt

  Public Overrides WriteOnly Property Name As String
    Set(Value As String)
      lblName.Text = Value
    End Set
  End Property



  Public Overrides WriteOnly Property OrderTotal As Decimal
    Set(Value As Boolean)
      lblOrderTotal.Text = Value
    End Set
  End Property



  Public Overrides WriteOnly Property OrderDescription As Decimal
    Set(Value As Boolean)
      lblOrderDescription.Text = Value
    End Set
  End Property
End Class

Ajudantes:

'
' FindNestedControl helpers based on tip by @andleer
' at http://stackoverflow.com/questions/619449/
'

Public Module Helpers
  <Extension>
  Public Function AllControls(Control As Control) As List(Of Control)
    Return Control.Controls.Flatten
  End Function



  <Extension>
  Public Function FindNestedControl(Control As Control, Id As String) As Control
    Return Control.Controls.Flatten(Function(C) C.ID = Id).SingleOrDefault
  End Function



  <Extension>
  Public Function FindNestedControl(Control As Control, Type As Type) As Control
    Return Control.Controls.Flatten(Function(C) C.GetType = Type).SingleOrDefault
  End Function



  <Extension>
  Public Function Flatten(Controls As ControlCollection) As List(Of Control)
    Flatten = New List(Of Control)

    Controls.Traverse(Sub(Control) Flatten.Add(Control))
  End Function


  <Extension>
  Public Function Flatten(Controls As ControlCollection, Predicate As Func(Of Control, Boolean)) As List(Of Control)
    Flatten = New List(Of Control)

    Controls.Traverse(Sub(Control)
                        If Predicate(Control) Then
                          Flatten.Add(Control)
                        End If
                      End Sub)
  End Function



  <Extension>
  Public Sub Traverse(Controls As ControlCollection, Action As Action(Of Control))
    Controls.Cast(Of Control).ToList.ForEach(Sub(Control As Control)
                                               Action(Control)

                                               If Control.HasControls Then
                                                 Control.Controls.Traverse(Action)
                                               End If
                                             End Sub)
  End Sub



  <Extension()>
  Public Function ToFormat(Template As String, ParamArray Values As Object()) As String
    Return String.Format(Template, Values)
  End Function



  <Extension()>
  Public Function ToHtml(Control As Control) As String
    Dim oSb As StringBuilder

    oSb = New StringBuilder

    Using oSw As New StringWriter(oSb)
      Using oTw As New HtmlTextWriter(oSw)
        Control.RenderControl(oTw)
        Return oSb.ToString
      End Using
    End Using
  End Function
End Module



Namespace Controls
  Public Class Styler
    Inherits LiteralControl

    Public Sub New(FileName As String)
      Dim _
        sFileName,
        sFilePath As String

      sFileName = Path.GetFileNameWithoutExtension(FileName)
      sFilePath = HttpContext.Current.Server.MapPath("~/Styles/{0}.css".ToFormat(sFileName))

      If File.Exists(sFilePath) Then
        Me.Text = "{0}<style type=""text/css"">{0}{1}</style>{0}".ToFormat(vbCrLf, File.ReadAllText(sFilePath))
      Else
        Me.Text = String.Empty
      End If
    End Sub
  End Class
End Namespace



Public Class Utils
  Public Shared Sub SendMail(Recipient As MailAddress, Subject As String, HtmlBody As String)
    Using oMessage As New MailMessage
      oMessage.To.Add(Recipient)
      oMessage.IsBodyHtml = True
      oMessage.Subject = Subject.Trim
      oMessage.Body = HtmlBody.Trim

      Using oClient As New SmtpClient
        oClient.Send(oMessage)
      End Using
    End Using
  End Sub
End Class
InteXX
fonte
0

Estou apenas adicionando a biblioteca que estou usando: https://github.com/lukencode/FluentEmail

Ele renderiza e-mails usando RazorLight , usa o estilo fluente para construir e-mails e oferece suporte a vários remetentes prontos para uso. Ele vem com métodos de extensão para ASP.NET DI também. Simples de usar, pouca configuração, com suporte a texto simples e HTML.

ahong
fonte