【问题标题】:Can I set up HTML/Email Templates with ASP.NET?我可以使用 ASP.NET 设置 HTML/电子邮件模板吗?
【发布时间】:2010-10-11 20:09:19
【问题描述】:

我正在开发一个会发送大量电子邮件的网站。我想设置页眉和页脚文本,甚至可以设置模板,以便用户在需要时轻松编辑这些电子邮件。

如果我将 HTML 嵌入到 C# 字符串文字中,那会很丑,而且他们不得不担心转义。包含页眉和页脚的平面文件可能会起作用,但感觉有些不对劲。

以某种方式使用.ASPX 页面作为模板,然后告诉我的代码来提供该页面,并使用为电子邮件返回的 HTML,什么是理想的选择。

有没有一种简单易用的方法来做到这一点?有没有更好的方法来解决这个问题?

更新:
我添加了一个答案,使您能够使用标准的 .aspx 页面作为电子邮件模板。只需像往常一样替换所有变量,使用数据绑定等。然后只需捕获页面的输出,瞧!你有你的 HTML 电子邮件!

已更新并附带警告!!!:
我在一些 aspx 页面上使用 MailDefinition 类就好了,但是当试图在正在运行的服务器进程期间使用这个类时,它失败了。我相信这是因为 MailDefinition.CreateMailMessage() 方法需要一个有效的控件来引用,即使它并不总是做某事。正因为如此,我会推荐我使用 aspx 页面的方法,或者 Mun 使用 ascx 页面的方法,这似乎更好一些。

【问题讨论】:

  • 另一种解决方案是使用 AlphaMail 使用 C# 和 Comlang 模板语言创建和发送电子邮件。
  • @JohnBubriski:我使用new System.Web.UI.Control() 解决了您在“已更新的警告”中提到的控制问题,如:mailDefinition.CreateMailMessage("somebody@fake.com", iDictionaryReplacements, new System.Web.UI.Control())
  • 是的,我也这样做了,但是考虑到 Razor 的出现,这已经不是一个好主意了。

标签: c# asp.net email templates


【解决方案1】:

这里已经有很多答案了,但我偶然发现了一篇关于如何将 Razor 与电子邮件模板结合使用的精彩文章。 Razor 是使用 ASP.NET MVC 3 推送的,但使用 Razor 不需要 MVC。这是做电子邮件模板的非常巧妙的处理

正如文章指出的那样,“Razor 的最佳之处在于,与它的前身(webforms)不同,它不与 Web 环境绑定,我们可以轻松地将它托管在 Web 之外,并将其用作各种目的的模板引擎。”

Generating HTML emails with RazorEngine - Part 01 - Introduction

Leveraging Razor Templates Outside of ASP.NET: They’re Not Just for HTML Anymore!

Smarter email templates in ASP.NET with RazorEngine

类似的 Stackoverflow 质量检查

Templating using new RazorEngine API

Using Razor without MVC

Is it possible to use Razor View Engine outside asp.net

【讨论】:

  • +1 但如果用户提供模板,请小心,因为他们可以从模板运行 C# 代码,从而在您的系统中提供比您可能想要的更多的功能。
  • 您对安全性有何看法。使用这个模板引擎可以格式化整个文件系统。我喜欢这个引擎,但这迫使我看看其他引擎。
【解决方案2】:

您可能还想尝试加载控件,然后将其呈现为字符串并将其设置为 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();

然后您可以像往常一样构建您的电子邮件:

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

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

您的用户控件可以包含其他控件,例如页眉和页脚,并且还可以利用数据绑定等功能。

【讨论】:

  • 我第一次不知何故错过了这个答案......很好。类似于我的解决方案,但使用 ascx 而不是 aspx。我仍然认为 aspx 会更好,因为它会提供一个完整的页面,而不是一个控件,但这正是我的想法。
  • 是的,您可以使用任一解决方案...它们的工作方式相同。这种方法的一个好处是一致性。例如,您可以通过重复使用相同的控件向用户显示订单摘要并在确认电子邮件中包含完全相同的内容。
  • 次要问题,但您缺少在第一个代码块中声明 StringBuilder 的行。
  • 示例中没有说明代码驻留在哪里,是页面吗?,因为LoadControl是页面/控件方法。
  • @Mun,您将用户控件加载到一个名为 ctrl 的变量中,并且您再也不会在代码中引用它。这应该如何工作?
【解决方案3】:

你可以试试MailDefinition class

【讨论】:

  • 我只想指出,这对基本电子邮件有好处,但不是任何复杂的东西。 MailDefinition 类不支持数据绑定。它唯一真正做的就是提供字符串替换。虽然,它也内置在会员帐户创建向导中。
  • MailDefinition 类必须获得一个控件才能呈现模板化内容。不太好。
【解决方案4】:

如果您想传递用户名、产品名称等参数,您可以使用开源模板引擎NVelocity 来生成最终的电子邮件/HTML。

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

在您的应用程序中通过 MailTemplate.vm 生成邮件正文:

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();

结果邮件正文为:

一个示例电子邮件模板 猩红花园

Foreach 示例:

[日期:2009 年 2 月 12 日] 名称:项目 1, 值:09

[日期:21.02.2009] 名称:项目 4, 值:52

[日期:01.03.2009] 名称:项目 2, 值:21

[日期:23.03.2009] 名称:第 6 项, 值:24

要编辑模板,也许您可​​以使用FCKEditor 并将您的模板保存到文件中。

【讨论】:

    【解决方案5】:

    Mail.dll email component 包含电子邮件模板引擎:

    这里是语法概述:

    <html>
    <body>
    Hi {FirstName} {LastName},
    
    Here are your orders: 
    {foreach Orders}
        Order '{Name}' sent to <strong>{Street}</strong>. 
    {end}
    
    </body>
    </html>
    

    以及加载模板、从 c# 对象填充数据并发送电子邮件的代码:

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

    您可以在email template engine 博文上获取更多信息。

    或者直接下载Mail.dll email component试试看。

    请注意,这是我创建的商业产品。

    【讨论】:

      【解决方案6】:

      如果灵活性是您的先决条件之一,那么 XSLT 可能是一个不错的选择,它完全受 .NET 框架的支持,您甚至可以让用户编辑这些文件。这篇文章 (http://www.aspfree.com/c/a/XML/XSL-Transformations-using-ASP-NET/) 可能对开始很有用(msdn 有更多关于它的信息)。 正如 ScarletGarden 所说,NVelocity 是另一个不错的选择,但我更喜欢 XSLT,因为它的“内置”.NET 框架支持和平台无关。

      【讨论】:

      • 我以前从未想过这一点,但在尝试了许多其他方法后,我发现这与将IXmlSerializable 接口添加到我的类中结合使用效果很好。只需几行,我就可以让我的班级收到一封电子邮件。
      • 呃,我一直在做关于 XSLT 的噩梦。可能是我使用过的最不直观的编程/标记语言。在您首次编写 XSLT 代码 1 个月后,无法为他人甚至您自己维护。
      【解决方案7】:

      我认为你也可以这样做:

      创建.aspx页面,并将其放在OnLoad方法的末尾,或者手动调用。

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

      我不确定这是否有任何潜在问题,但它看起来会起作用。这样,您可以使用功能齐全的 .aspx 页面,而不是仅支持文本替换的 MailDefinition 类。

      【讨论】:

      • 虽然 MailDefinition 类是一个好的开始,但它有点初级。这种方法应该支持更多的特性,比如数据绑定,甚至可能是跟踪。对此有何想法或潜在的陷阱?
      • 太棒了!你有什么问题吗?
      • 所以当您的用户需要更改邮件模板时,您要让他们编辑 .aspx 文件吗?我认为这是一个潜在的问题。
      • 我不这么认为,至少,不会比他们可以编辑的其他模板更大的风险。当然,如果他们知道自己在做什么,他们可能会造成伤害,但至少在这种情况下,不太可能。它不会是一个复杂的 .aspx 页面,更像是一个带有占位符的模板。
      • 已经有一段时间了,我知道,但您还记得您的最终解决方案吗?我无法让这种特殊的方法与Page 一起工作,至少在使用通用扩展方法进行渲染时是这样。因此我切换到UserControl;请参阅下面的答案。到目前为止,它似乎运作良好......我很想知道你当时是如何解决的。
      【解决方案8】:

      当然你可以创建一个 html 模板,我也推荐一个文本模板。在模板中,您可以将 [BODY] 放在要放置正文的位置,然后您可以阅读模板并将正文替换为新内容。你可以发送 使用 .Nets Mail Class 的电子邮件。最初创建电子邮件后,您只需循环将电子邮件发送给所有收件人。对我来说就像一个魅力。

      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("address@gmail.com", "display name");
      m.To.Add("address@gmail.com");
      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);
      

      【讨论】:

      • 你可以把 StreamReader 的东西删掉,换成 File.ReadAllText(path)
      • 这是一个好的开始,但只提供页眉和页脚的功能。这对身体本身并没有真正的帮助。
      • 正文 您只需在 HTMLBody 和 TextBody 字段中输入所需的正文内容,当然也可以将它们存储在文件中
      【解决方案9】:

      这里是另一种使用 XSL 转换来处理更复杂的电子邮件模板的替代方法:Sending HTML-based email from .NET applications

      【讨论】:

      • 喜欢这个链接。谢谢!我的大脑开始转动并意识到我可以更进一步,并拥有一个 XSLT 模板,该模板将 XML Serializable 对象或 WCF Data Contract 直接转换为 html-email 格式。突然间,我会通过实际的可序列化类拥有“强类型”电子邮件模板!
      【解决方案10】:

      这样做时要小心,SPAM 过滤器似乎会阻止 ASP.net 生成的 html,显然是因为 ViewState,所以如果您要这样做,请确保生成的 Html 是干净的。

      我个人会考虑使用 Asp.net MVC 来实现您想要的结果。或者NVelocity 很擅长这个

      【讨论】:

        【解决方案11】:

        以某种方式使用 .ASPX 页面作为模板,然后告诉我的代码为该页面提供服务,并使用为电子邮件返回的 HTML,什么是理想的选择。

        您可以轻松地构造一个 WebRequest 来访问 ASPX 页面并获得生成的 HTML。多做一些工作,您可能无需 WebRequest 就可以完成它。 PageParser 和 Response.Filter 将允许您运行页面并捕获输出......尽管可能有一些更优雅的方式。

        【讨论】:

          【解决方案12】:

          我对其中一个项目有类似的要求,您必须每天发送大量电子邮件,而客户希望完全控制不同类型电子邮件的 html 模板。

          由于要发送大量电子邮件,因此性能是首要考虑因素。

          我们想出的是 sql server 中的静态内容,您可以在其中保存整个 html 模板标记(以及占位符,如 [UserFirstName]、[UserLastName],它们在运行时被真实数据替换)用于不同类型的电子邮件

          然后我们将这些数据加载到 asp.net 缓存中 - 所以我们不会一遍又一遍地读取 html 模板 - 但只有在它们实际更改时才可以

          我们为客户提供了一个所见即所得的编辑器,以便通过管理 Web 表单修改这些模板。每当进行更新时,我们都会重置 asp.net 缓存。

          然后我们有一个单独的电子邮件日志表 - 每个要发送的电子邮件都被记录下来。该表有名为 emailType、emailSent 和 numberOfTries 的字段。

          对于需要尽快发送的重要电子邮件类型(例如新会员注册、忘记密码),我们只需每 5 分钟运行一次工作

          我们每 15 分钟针对不太重要的电子邮件类型(如促销电子邮件、新闻电子邮件等)运行另一项工作

          这样您就不会阻止您的服务器发送不间断的电子邮件,并且您可以批量处理邮件。发送电子邮件后,您将 emailSent 字段设置为 1。

          【讨论】:

          • 但是您是如何处理收藏的?
          • 我也这样做了,效果很好。此外,如果报告是您的事,您还可以回顾历史并查看已发送电子邮件的记录。
          【解决方案13】:

          请注意,aspx 和 ascx 解决方案需要当前的 HttpContext,因此不能在没有大量工作的情况下异步使用(例如在线程中)。

          【讨论】:

            【解决方案14】:

            我认为简单的答案是 MvcMailer。它是 NuGet 包,可让您使用自己喜欢的视图引擎生成电子邮件。请参阅 NuGet 包 hereproject documentation

            希望对你有帮助!

            【讨论】:

            • 嗯,奇怪的是这个答案没有得到那么多关注?!
            【解决方案15】:

            DotLiquid 是另一种选择。您将类模型中的值指定为{{ user.name }},然后在运行时提供该类中的数据以及带有标记的模板,它会为您合并这些值。它在许多方面类似于使用 Razor 模板引擎。它支持更复杂的东西,如循环和 ToUpper 等各种功能。好消息是这些是“安全的”,因此创建模板的用户不会像在 razor 中那样使您的系统崩溃或编写不安全的代码:http://dotliquidmarkup.org/try-online

            【讨论】:

              【解决方案16】:

              如果您能够允许 ASPNET 和相关用户读取和写入文件的权限,您可以轻松地使用带有标准 String.Format() 占位符({0}{1:C} 等)的 HTML 文件来完成此操作.

              使用System.IO 命名空间中的类,仅以字符串形式读取文件。获得该字符串后,将其作为第一个参数传递给String.Format(),并提供参数。

              保留该字符串,并将其用作电子邮件的正文,基本上就完成了。我们今天在几十个(诚然很小的)网站上执行此操作,并且没有遇到任何问题。

              我应该指出,如果 (a) 您不是一次发送数以万计的电子邮件,(b) 您没有对每封电子邮件进行个性化处理(否则您会占用大量的字符串),这种方法效果最好(c) HTML 文件本身比较小。

              【讨论】:

                【解决方案17】:

                设置设置Email Message IsBodyHtml = true

                获取包含您的电子邮件内容的对象序列化对象 并使用 xml/xslt 生成 html 内容。

                如果你想做 AlternateViews 做同样的事情,jmein 只使用不同的 xslt 模板来创建纯文本内容。

                这样做的主要优点之一是,如果您想更改布局,只需更新 xslt 模板。

                【讨论】:

                  【解决方案18】:

                  看看 SubSonic (www.subsonicproject.com)。他们这样做是为了生成代码——模板是标准的 ASPX,它输出 c#。相同的方法可以在您的场景中重复使用。

                  【讨论】:

                    【解决方案19】:

                    我会使用像 TemplateMachine 这样的模板库。这允许您将电子邮件模板与普通文本放在一起,然后根据需要使用规则注入/替换值。与 Ruby 中的 ERB 非常相似。这使您可以将邮件内容的生成分开,而不会将您与 ASPX 等内容过于紧密地联系在一起。然后,一旦使用此内容生成了内容,您就可以通过电子邮件发送出去。

                    【讨论】:

                      【解决方案20】:

                      我喜欢 Raj 的回答。像 ListManager 这样的程序和像 DNN 这样的框架做类似的事情,如果需要由非技术用户轻松编辑,所见即所得的编辑器来修改存储在 SQL 中的 HTML 是一种最简单、直接的方法,并且可以轻松地独立于页脚编辑页眉,等,以及使用令牌动态插入值。

                      如果使用上述方法(或任何方法,真的),要记住的一件事是严格和小心您允许编辑器插入哪些类型的样式和标签。如果您认为浏览器很挑剔,请等到您看到电子邮件客户端呈现相同内容的不同之处...

                      【讨论】:

                        【解决方案21】:

                        与 Canavar 的回答类似,但我总是使用“StringTemplate”而不是 NVelocity 我从配置文件加载模板,或使用 File.ReadAllText() 加载外部文件并设置值。

                        这是一个 Java 项目,但 C# 端口是可靠的,我已经在几个项目中使用过它(只是将它用于使用外部文件中的模板进行电子邮件模板化)。

                        替代品总是好的。

                        【讨论】:

                          【解决方案22】:

                          这是一个使用 WebClient 类的简单方法:

                          public static string GetHTMLBody(string url)
                          {
                              string htmlBody;
                          
                              using (WebClient client = new WebClient ())
                              {
                                  htmlBody = client.DownloadString(url);
                              }
                          
                              return htmlBody;
                          }
                          

                          那就这样称呼它吧:

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

                          当然,您的 CSS 需要内联,以便在大多数电子邮件客户端(例如 Outlook)中显示网页样式。如果您的电子邮件显示动态内容(例如客户姓名),那么我建议在您的网站上使用 QueryStrings 来填充数据。 (例如http://www.yourwebsite.com?CustomerName=Bob

                          【讨论】:

                          • 很好,尽管我认为大多数其他答案都无需向网站发出网络请求,即必须在您的网站上托管电子邮件正文。
                          • @Rup 可以理解,但请记住,很多时候人们都希望看到电子邮件的“Web 版本”。该解决方案非常适合这种情况。
                          【解决方案23】:

                          @bardev 提供了一个很好的解决方案,但不幸的是它并不是在所有情况下都是理想的。我的就是其中之一。

                          我在 VS 2013 中在网站中使用 WebForms(我发誓我再也不会使用网站了——真是个 PITA)。

                          我尝试了 Razor 的建议,但作为一个网站,我没有获得 IDE 在 MVC 项目中提供的最重要的 IntelliSense。我还喜欢在我的模板中使用设计器——这是 UserControl 的完美选择。

                          Nix 再次使用 Razor。

                          所以我想出了这个小框架(@mun 用于 UserControl,@imatoria 用于强类型)。我能看到的唯一潜在的麻烦点是你必须小心保持你的 .ASCX 文件名与其类名同步。如果你迷路了,你会得到一个运行时错误。

                          FWIW:在我的测试中,至少 RenderControl() 调用不喜欢 Page 控件,所以我选择了 UserControl。

                          我很确定我已经在这里包含了所有内容;如果我遗漏了什么,请告诉我。

                          HTH

                          用法:

                          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} <james.bond@mi6.co.uk>".ToFormat(oTemplate.Name), "Purchase Receipt", oTemplate.ToHtml)
                            End Sub
                          End Class
                          

                          基类:

                          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
                          

                          “工厂”类:

                          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
                          

                          模板类:

                          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
                          

                          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>
                          

                          ASCX 页脚:

                          <%@ Control Language="VB" ClassName="_Footer" %>
                          
                          </body>
                          </html>
                          

                          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" />
                          

                          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
                          

                          助手:

                          '
                          ' 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
                          

                          【讨论】:

                            【解决方案24】:

                            将我正在使用的库加入其中:https://github.com/lukencode/FluentEmail

                            它使用RazorLight 呈现电子邮件,使用流利的风格构建电子邮件,并支持开箱即用的多个发件人。它也带有 ASP.NET DI 的扩展方法。使用简单,设置少,支持纯文本和 HTML。

                            【讨论】:

                              猜你喜欢
                              • 2018-02-23
                              • 2020-05-08
                              • 2014-03-20
                              • 1970-01-01
                              • 2010-12-16
                              • 2011-06-14
                              相关资源
                              最近更新 更多