Reporting Services 2008 代码创建订阅后未保存
结论
代码先调用 CreateBatch,再把返回的 BatchHeader 传给 CreateSubscription。这意味着创建订阅的操作只加入了批处理,并未立即提交。由于最后没有调用 ExecuteBatch,Subscriptions 表中不会出现相应记录。
CreateSubscription 返回 subscription ID,不代表数据库事务已经提交。这个 ID 可以在执行批处理之前生成。
正确的调用顺序是:
- 调用
CreateSubscription。 - 调用
ExecuteBatch(bh)提交批处理。 - 如果需要立即测试,在批处理提交后单独调用
FireEvent。
建议修改
同时,把报表参数数组从 10 个元素改为实际使用的 3 个元素。原数组剩余的 7 个元素都是 null,可能在序列化或执行批处理时引发错误。
void SendReportEmail()
{
RSServiceReference.ReportingService2005SoapClient rs =
new RSServiceReference.ReportingService2005SoapClient();
rs.ClientCredentials.Windows.AllowedImpersonationLevel =
System.Security.Principal.TokenImpersonationLevel.Impersonation;
string batchID;
RSServiceReference.ServerInfoHeader infoHeader =
rs.CreateBatch(out batchID);
RSServiceReference.BatchHeader batchHeader =
new RSServiceReference.BatchHeader
{
BatchID = batchID,
AnyAttr = infoHeader.AnyAttr
};
string report = "/PCMSR6Reports/PaymentRequestStatusMIS";
string description = "Send report by email";
string eventType = "TimedSubscription";
// 必须使用未来时间;下面只是格式示例。
string scheduleXml =
"<ScheduleDefinition " +
"xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" " +
"xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">" +
"<StartDateTime xmlns=\"http://schemas.microsoft.com/sqlserver/2006/03/15/reporting/reportingservices\">" +
"2026-09-01T15:15:00+08:00" +
"</StartDateTime>" +
"</ScheduleDefinition>";
RSServiceReference.ParameterValue[] extensionParams =
new RSServiceReference.ParameterValue[]
{
new RSServiceReference.ParameterValue
{
Name = "TO",
Value = "Hisham@comsoft.com"
},
new RSServiceReference.ParameterValue
{
Name = "IncludeReport",
Value = "True"
},
new RSServiceReference.ParameterValue
{
Name = "RenderFormat",
Value = "MHTML"
},
new RSServiceReference.ParameterValue
{
Name = "Subject",
Value = "@ReportName was executed at @ExecutionTime"
},
new RSServiceReference.ParameterValue
{
Name = "Comment",
Value = "Here is your test report"
},
new RSServiceReference.ParameterValue
{
Name = "IncludeLink",
Value = "True"
},
new RSServiceReference.ParameterValue
{
Name = "Priority",
Value = "NORMAL"
}
};
RSServiceReference.ParameterValue[] parameters =
new RSServiceReference.ParameterValue[]
{
new RSServiceReference.ParameterValue
{
Name = "BranchId",
Value = "1"
},
new RSServiceReference.ParameterValue
{
Name = "UserName",
Value = "admin"
},
new RSServiceReference.ParameterValue
{
Name = "SupplierId",
Value = "0"
}
};
RSServiceReference.ExtensionSettings extensionSettings =
new RSServiceReference.ExtensionSettings
{
Extension = "Report Server Email",
ParameterValues = extensionParams
};
try
{
string subscriptionId;
rs.CreateSubscription(
batchHeader,
report,
extensionSettings,
description,
eventType,
scheduleXml,
parameters,
out subscriptionId);
// 关键步骤:真正执行并提交前面加入批处理的操作。
rs.ExecuteBatch(batchHeader);
Console.WriteLine(
"Subscription created: " + subscriptionId);
// TimedSubscription 本来会由计划触发。
// 如需立即测试,可在批处理提交后再调用 FireEvent。
// 具体代理签名可能因生成方式不同而略有差异。
//
// rs.FireEvent(null, "TimedSubscription", subscriptionId);
}
catch (System.ServiceModel.FaultException ex)
{
Console.WriteLine("SOAP fault: " + ex.Message);
throw;
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
throw;
}
}
为什么没有出现异常
在批处理模式下,CreateSubscription 只是把操作加入当前 batch。数据库写入和相关验证要等到调用 ExecuteBatch 时才会执行。
因此,缺少 ExecuteBatch 时可能出现以下情况:
CreateSubscription返回了 subscription ID;- 调用过程没有出现异常;
ReportServer.dbo.Subscriptions中没有记录;- SQL Server Agent 没有可执行的订阅计划;
- 邮件不会发送。
计划 XML 无效、参数错误或扩展设置不完整等问题,也可能要到执行 ExecuteBatch 时才会暴露。
FireEvent 不能替代 ExecuteBatch
FireEvent 会向报表服务器的事件队列写入事件,用于触发已经存在的订阅。它不能提交 batch,也无法让尚未保存的订阅写入数据库。
调用顺序应为:
CreateBatch
↓
CreateSubscription
↓
ExecuteBatch
↓
可选:FireEvent
如果只需要创建定时订阅,无须调用 FireEvent。计划时间到达后,Reporting Services 会自动触发订阅。
还需要检查的项目
提交批处理后,如果订阅已经写入数据库,但仍未收到邮件,可以继续检查以下内容。
1. 计划时间必须有效
示例中的时间是:
2010-03-06T15:15:00.000+05:00
这个时间已经过去。实际运行时应设置未来时间,并确认时区偏移与报表服务器所在时区一致。否则,即使订阅创建成功,也未必会再次执行。
2. 检查计划 XML 的命名空间
代码使用 ReportingService2005SoapClient,而 ScheduleDefinition 中的命名空间是:
http://schemas.microsoft.com/sqlserver/2006/03/15/reporting/reportingservices
具体使用哪个命名空间,取决于实际连接的 SSRS Web Service 端点,以及生成代理时使用的 WSDL,不能只根据产品名称判断。可以通过以下方式确认:
- 检查服务引用是否指向正确的
ReportService2005.asmx; - 查看生成代理中
ScheduleDefinition类型的 XML 命名空间; - 直接创建
ScheduleDefinition对象并序列化,避免手写 XML。
如果命名空间或计划结构不符合服务要求,通常会在执行 ExecuteBatch 时收到 SOAP fault。
3. 确认报表参数完整
参数数组只应包含实际使用的元素,不要创建长度为 10 的数组后只赋值 3 项:
new ParameterValue[10]
应改为:
new ParameterValue[3]
还要确认所有必填的报表参数均已提供,并且参数名的大小写和数据格式与报表定义一致。
4. 检查订阅执行状态
订阅创建成功后,可以通过 Web Service 的订阅查询接口或 Report Manager 查看状态。排查时应检查:
Subscriptions中是否存在记录;LastStatus是否包含发送错误;- SQL Server Agent 服务是否正在运行;
- SSRS 日志中是否有调度或邮件扩展错误;
- 邮件投递扩展的 SMTP 配置是否有效。
不要直接修改 ReportServer 数据库表。该数据库属于 Reporting Services 的内部实现,应通过 SOAP API 或管理工具操作。
最关键的修复
原代码至少要在 CreateSubscription 后增加:
rs.ExecuteBatch(bh);
如果需要调用 FireEvent,应将它移到 ExecuteBatch 之后。如果只需要按计划发送,则可以直接删除 FireEvent。完成批处理提交后,订阅才会真正写入 ReportServer 数据库。
备注:内容仅供参考。
