微软BI 之SSIS 系列 - 使用 Script Task 访问非 Windows 验证下的 SMTP 服务器发送邮件

开篇介绍

大多数情况下我们的 SSIS 包都会配置在 SQL Agent Job 中周期性的按计划执行,比如每天晚上调用 SSIS 包刷新数据,处理 Cube 等。一旦 SSIS 包中出现任何异常,报错,那么配置在 SQL Agent Job 中的通知,邮件提醒就会把这些错误信息发邮件到指定的用户或者系统维护者,这样就起到了一个错误监控的作用。

但是在有的情况下,有一些自定义的 SSIS 调度框架的计划调度都不是通过 SQL Agent Job 配置来完成的。比如我以前在一个小项目中设计过一个 SSIS 调度框架,只有一个主包配置在 SQL Agent Job 中,所有子包的上线不由 SQL Agent Job 支配。每次新增的子包要上线,只需要将相应的信息以及调度计划注册到相应的表中即可,不需要打开 SQL Agent Job 调整各个子包的执行顺序等等。那么在这种情况下,就只能在 SSIS 内部使用发送邮件的功能来发送错误信息,因为所有的子包和 SQL Agent Job 都是没有直接关系的。

在 SSIS 中我们可以通过 Send Mail Task 或者 Script Task 来发送邮件,当然还有第三种方式就是写存储过程调用发送邮件存储过程.

Send Mail Task 发送邮件

Send Mail Task 的使用其实非常的简单,配置的步骤也并不多,但是在使用它的时候有几个限制:

  1. 只能发送普通的文本格式的邮件,也就是说不支持 HTML 格式的邮件。
  2. 连接 SMTP Server 时不支持用户填写用户名和密码,也就是说用户要么在域环境下 SMTP Server 对用户进行 Windows 方式验证,要么就是访问的 SMTP Server 支持匿名访问,不需要提供用户名和密码。
  3. SMTP Server 的端口号使用默认的,也就是说如果在域中的 SMTP Server 端口号不是默认的话 Send Mail Task 就无法使用了。

这里是我常用的一个测试服务器地址 http://www.yopmail.com/en/ ,免费的并支持匿名访问,可以通过它来进行邮件发送的测试。关于 Send Mail Task 的使用方式就不在这里演示了,在非域和非 Windows 验证方式下用我提到的可匿名访问的 Yop Mail 一试便知,主要是讲 Script Task 发送邮件的方式。

Script Task 发送邮件

新建一个包并首先创建一个 SMTP 连接 - New Connection。

选择 SMTP 连接管理器。

以我的邮箱为例子 [email protected] 发送邮箱服务器是 smtp.126.com,这个可以在网易邮箱配置页面查到。但是非常遗憾!在这里仍然是没有地方可以配置用户名和密码的,默认的情况下仍然是只支持 Windows 验证或者匿名访问。

所以说,如果想要在 SMTP Connection 里自己动手配置用户名和密码是不可能的,那么像这种 163,126等等很多很多邮箱直接通过 SMTP Connection 是肯定无法完成发送邮件操作的。

看下图时一定要理解一点:几乎每一个 SSIS 控件的属性都是可配置的,也是可获取的,这是我们在 SSIS ETL 的学习过程中解决很多很多问题的最关键的地方!

新建一些常用的变量,发件人,收件人,抄送,邮件标题,内容,附件地址,还有用户名和密码等。

PS:关于这个密码有一个故事,以前在公司里有一个管机房管电脑的同事,他的网络下载速度最快也没人管,他的电脑里有各种游戏,高清蓝光和 1024。每次大家伙要点东西像挤牙膏似的,大家都在想办法搞他的密码,直到后来大家有意无意记了并试出了密码 - 大概就是 lowformat1MBD!! 类似于这样的!看了密码之后我们都一致认为这个密码确实很符合他的职业,不过貌似苦大仇深啊!

其中 PV_CONTENT 变量描述的是邮件发送的 HTML 格式的内容,注意是字符串类型的因此需要双引号。

"<h1>Hi BIWORK!</h1>
<p>
This is a test email from the test SSIS package of BIWORK!
</p>
<p>Server Name - "+ @[System::MachineName] +"</p>
<p>Package Name - "+ @[System::PackageName] +"</p>
<p>Package Start Time - "+ (DT_WSTR, 12) ( DT_DBDATE) @[System::StartTime] +"</p>
<p></br>Thanks and Regards!</p>
<p>Simon</p>"

系统变量的信息也通过表达式嵌入到邮件正文中。

编辑 Script Task 并引入这些变量,Script Task 中的代码 -

using System.Net.Mail; // BIWORK Added
using System.Text.RegularExpressions; // BIWORK Added

Main 中的代码包括处理优先级,多个收件人的处理,用户名和密码,添加附件的方式等等。

public void Main()
        {
            // Default Priority and SMTP Server Port
            int iPriority = 1;
            int smtpPort = 25;

            //User::PV_ATTACHED_FILE,User::PV_CONTENT,User::PV_MAIL_FROM,
            //User::PV_MAIL_TO,User::PV_MAIL_TO_CC,User::PV_TITLE
            String mailFrom = Dts.Variables["User::PV_MAIL_FROM"].Value.ToString();
            String mailTo = Dts.Variables["User::PV_MAIL_TO"].Value.ToString();
            String mailToCC = Dts.Variables["User::PV_MAIL_TO_CC"].Value.ToString();
            String mailTitle = Dts.Variables["User::PV_TITLE"].Value.ToString();
            String mailContent = Dts.Variables["User::PV_CONTENT"].Value.ToString();
            String mailAttachedFilePath = Dts.Variables["User::PV_ATTACHED_FILE"].Value.ToString();

            // User Information
            String loginUser = Dts.Variables["User::PV_LOGIN_USER"].Value.ToString();
            String loginPwd = Dts.Variables["User::PV_LOGIN_PWD"].Value.ToString();

            // Get SMTP Server Information from Connection Manager
            String smtpServer = Dts.Connections["CM_SMTP_126"].Properties["SmtpServer"].GetValue(Dts.Connections["CM_SMTP_126"]).ToString();

            try
            {
                SmtpClient smtpClient = new SmtpClient();
                MailMessage message = new MailMessage();
                MailAddress fromAddress = new MailAddress(mailFrom, "BIWORKTEST");

                string[] sEmailTo = Regex.Split(mailTo, ";");
                string[] sEmailToCC = Regex.Split(mailToCC, ";");

                smtpClient.Host = smtpServer;
                smtpClient.Port = smtpPort;

                // Login information
                System.Net.NetworkCredential myCredentials =
                   new System.Net.NetworkCredential(loginUser, loginPwd);
                smtpClient.Credentials = myCredentials;

                // Attachment
                System.Net.Mail.Attachment attachment;
                attachment = new System.Net.Mail.Attachment(mailAttachedFilePath);
                message.Attachments.Add(attachment);

                message.From = fromAddress;  

                // Multiple email to address
                if (sEmailTo != null)
                {
                    for (int i = 0; i < sEmailTo.Length; ++i)
                    {
                        if (sEmailTo[i] != null && sEmailTo[i] != "")
                        {
                            message.To.Add(sEmailTo[i]);
                        }
                    }
                }

                // Multiple cc address
                if (sEmailToCC != null)
                {
                    for (int i = 0; i < sEmailToCC.Length; ++i)
                    {
                        if (sEmailToCC[i] != null && sEmailToCC[i] != "")
                        {
                            message.To.Add(sEmailToCC[i]);
                        }
                    }
                }

                // Email priority
                switch (iPriority)
                {
                    case 1:
                        message.Priority = MailPriority.High;
                        break;
                    case 3:
                        message.Priority = MailPriority.Low;
                        break;
                    default:
                        message.Priority = MailPriority.Normal;
                        break;
                }

                message.Subject = mailTitle;
                message.IsBodyHtml = true;
                message.Body = mailContent;

                smtpClient.Send(message);
            }catch (Exception ex)
            {
                Dts.TaskResult = (int)ScriptResults.Failure;
            }
            // Close Script Task with success
            Dts.TaskResult = (int)ScriptResults.Success;
        }

保存并执行 SSIS 包,执行成功!

优先级别,标题,发送人等信息 -

附件信息和内容都可以看的到,包括在抄送栏中抄送给了自己,Package Start Time 的时间是来自虚机的时间,我虚机的时间还是 28日,这个是正确的 -

总结

从中可以看出,虽然在 SSIS 中提供了 SMTP 的连接方式,但是实际上它还是默认只允许通过 Windows 验证或者匿名访问的方式来访问。但是我们通过 Script Task 在 Script 中关联到了 SMTP 服务器,并同时写入用户名和密码,这样就实现了在 SSIS 中访问非域环境下第三方的 SMTP 服务器并通过身份验证且发送邮件的效果。

当然,这种方式在实际项目中可能用的并不是很多,因为项目中的邮件肯定是通过域验证的方式去访问更安全一些,毕竟系统信息的传递只能限制在域中。

在项目中使用邮件通知如是 Windows 验证的方式,有这几个点需要注意:

  1. 在开发与测试过程中,手动执行 SSIS Package 的这个用户要有域中发送邮件的权限,能够通过 Windows 验证。
  2. 当包部署到 SQL Agent Job 中后,执行 SQL Agent Job 的账号要有域发送邮件的权限,也必须能通过 Windows 验证。
  3. 如果不需要部署到 SQL Agent Job,那么就直接使用 Send Mail Task 通过 Windows 验证也可以直接发送错误消息提醒。

疑问

在这个例子中的代码是不是一定要像这样去访问 SMTP Server 的连接信息,感觉是不是多此一举? - 可以不用这样访问,其实很多 SMTP 的配置信息最为方便的是配置成变量,通过变量传递给 Script Task 就可以了。这里通过这样的一种方式来表示在 Script Task 中如何获取连接管理器的属性信息的,因为很多时候我们只知道如何去获取变量信息,这一点体现了 C# Script 的灵活性,我们在 Script 中可以做非常多的事情。包括这些访问 SMTP Server 如何使用 smtpClient 发送邮件的代码可以在网上找到现成的很多很多示例,而我们要做的事情就是大胆的灵活运用它们来解决我们的问题。

在我的这些文章中也提到了邮件处理相关的内容

微软BI 之SSRS 系列 - 报表邮件订阅中 SMTP 服务器匿名访问与 Windows验证, 以及如何成功订阅报表的实例

更多 BI 文章请参看 BI 系列随笔列表 (SSIS, SSRS, SSAS, MDX, SQL Server)  如果觉得这篇文章看了对您有帮助,请帮助推荐,以方便他人在 BIWORK 博客推荐栏中快速看到这些文章。

时间: 2024-10-10 08:38:25

微软BI 之SSIS 系列 - 使用 Script Task 访问非 Windows 验证下的 SMTP 服务器发送邮件的相关文章

微软BI 之SSIS 系列 - 使用 Script Component Destination 和 ADO.NET 解析不规则文件并插入数据

开篇介绍 这一篇文章是 微软BI 之SSIS 系列 - 带有 Header 和 Trailer 的不规则的平面文件输出处理技巧 的续篇,在上篇文章中介绍到了对于这种不规则文件输出的处理方式.比如下图中的这种不规则文件,第一行,第二行 Header 部分,第三行的内容 Content 部分,最后一行的 Trailer 部分. 在前几个课程 微软BI SSIS 2012 ETL 控件与案例精讲 第43,44,45,46 课中,我分别讲解了如何使用 .Script Component Source 解

微软BI 之SSIS 系列 - MVP 们也不解的 Scrip Task 脚本任务中的一个 Bug

开篇介绍 前些天自己在整理 SSIS 2012 资料的时候发现了一个功能设计上的疑似Bug,在 Script Task 中是可以给只读列表中的变量赋值.我记得以前在 2008 的版本中为了弄明白这个配置,还特意测试过这个细节,获取错误并理解了这个功能.但是现在回去再次测试 2008 的版本时,发现这个功能在 2008 中其实也是错误的,把我印象中的测试结果完全给推翻了,所以到现在已经搞不清楚我当时到底是如果得出这个错误的. 疑似功能 Bug 描述 在 SSIS 包中定义了用户自定义变量 - PV

微软BI 之SSIS 系列 - Execute SQL Task 中的 Single Row 与 Full Result Set 的处理技巧

开篇介绍 Execute SQL Task 这个控件在微软BI ETL 项目中使用的频率还是非常高的,也是大部分入门 SSIS 初学者最早接触到的几个控制流控件. 我们通常使用 Execute SQL Task 的场景包含但不止于以下几类: 在从源端加载数据到 Staging 表之前使用 Execute SQL Task 执行一些 Truncate 操作. 执行一些 Log 的插入,更新操作. ETL 过程中的 Merge 语句操作. XML 的输出处理. 关于如何使用 Execute SQL

微软BI 之SSIS 系列 - 在 SSIS 中导入 ACCESS 数据库中的数据

开篇介绍 来自 天善学院 一个学员的问题,如何在 SSIS 中导入 ACCESS 数据表中的数据. 在 SSIS 中导入 ACCESS 数据库数据 ACCESS 实际上是一个轻量级的桌面数据库,直接使用文件形式存储.在国内大量使用 ACCESS 作为 BI 数据源并不多,但是在国外特别是美国使用的还比较多,因为他们的 IT 基础起步比较早.在我的第一个美国的医疗保险项目中,就遇到过大量的 ACCESS 数据源,前后总共有 500 多个 ACCESS 表.而现在从国外一些朋友反馈的情况仍然还有在使

微软BI 之SSIS 系列 - Lookup 中的字符串比较大小写处理 Case Sensitive or Insensitive

前几天碰到这样的一个问题,在 Lookup 中如何设置大小写不敏感比较,即如何在 Lookup 中的字符串比较时不区分大小写? 实际上就这个问题已经有很多人提给微软了,但是得到的结果就是 Closed and Won’t fix. 说白了,这个就是 By Design,包括到现在的 2012 也没有这个配置选项. https://connect.microsoft.com/SQLServer/feedback/details/339069/ssis-case-sensitive-data-flo

微软BI 之SSIS 系列 - 带有 Header 和 Trailer 的不规则的平面文件输出处理技巧

案例背景与需求介绍 之前做过一个美国的医疗保险的项目,保险提供商有大量的文件需要发送给比如像银行,医疗协会,第三方服务商等.比如像与银行交互的 ACH 文件,传送给协会的 ACH Credit 等文件.这些文件格式在美国都是开放的,通用的,可以直接到相关网站下载.也就是说像银行,协会等他们接受这种固定格式的文件,读取数据,读取公司编号进行业务来往或者记录.我当时就是直接在网上搜索到一个 PDF 格式的文件说明,大概有10来页,就是告诉你这个格式是如何定义,应该如何来处理的. 那么这种文件并非像我

微软BI 之SSIS 系列 - 对于平面文件中 NULL 值处理过程中容易极易混淆的几个细节

最近有人问我 OLE DB Destination 中的 Keep Nulls 如何控制 NULL 值的显示,为什么选中了 Keep Nulls 但是数据库中没有 NULL 值? 为什么在 Flat File Source 中勾选上了 Retain null values from the source as null values in the data flow 但是为什么目标表上显示的是一个当前日期,而不是 NULL 值等等,单开此文来解释这些非常容易混淆的概念. 在比较纯粹的 ETL 项

微软BI 之SSIS 系列 - 再谈Lookup 缓存

开篇介绍 关于 Lookup 的缓存其实在之前的一篇文章中已经提到了 微软BI 之SSIS 系列 - Lookup 组件的使用与它的几种缓存模式 - Full Cache, Partial Cache, NO Cache 但是还是可能遗漏的部分内容,因此在这里重新总结并补充一下.这是第一篇,还是从理论的角度来讨论 Lookup 缓存的问题:后面有空还会再写一篇,从后台 SQL 执行的情况来理解 Lookup 的工作过程. 并且关于 Lookup 缓存还有其它比较有意思的话题,比如我的这些帖子,大

微软BI 之SSIS 系列 - Precedence Constraint 详解优先约束的使用

开篇介绍 Precedence Constraint 优先约束 - 在控制流中使用,用来链接控制流中各种 Task,Container,并且要求满足一定的条件才能执行相关联的 Task 或者 Container. 比如下图中,第一个 Execute SQL Task 叫做 Precedence-Executable 优先可执行任务,而Script Task 由于在关联箭头的下游,所以它叫做 Constrained-Executable 受约束可执行任务.关联箭头的上游任务自然先执行,关联箭头下方