1

我继承了一个使用 Microsoft Web ReportViewer 在本地运行小型报告的应用程序。我们的应用程序允许您通过单击将用户路由到允许他们以 PDF 格式下载报告的 URL 的特定按钮来“预览/打印”报告。我们最近收到了将这些 PDF 保存到我们数据库中的文档表的要求。我已经能够让它在 localhost 上成功运行;但是,当我将应用程序发布到我们的 IIS 服务器时,我收到以下错误:

System.Data.SqlClient.SqlException: Login failed for user 'Domain\Servername$'.

我已经查看了所有我能找到的涉及此错误的站点(包括这个)——最重要的是将服务器帐户添加到 SQL 数据库;但是,这应该不是问题,因为预览/打印文档的按钮仍然可以正常工作,并且在应用程序发布并且所有数据都保存在本地对象中时按预期工作,该本地对象之前是从数据库中提取的(下面的模型参数)。按钮和自动生成功能使用相同的两种方法来创建 PDF 文档(见下文)。

这是一些代码:

   public static byte[] CreatePDFDocument(DocumentTemplateType template, Request model)
   {
        Warning[] warnings;
        string[] streamIds;
        string mimeType = string.Empty;
        string encoding = string.Empty;
        string extension = string.Empty;

        ReportViewer viewer = new ReportViewer();

        viewer.ProcessingMode = ProcessingMode.Local;
        viewer.LocalReport.ReportEmbeddedResource = "Xxx.Xxx.Bll.ReportViewerRDLCs." + template.RdlcFilename;

        switch ((DocumentType)template.DocumentTypeId)
        {
            case eDocumentType.Report1:
                viewer.LocalReport.SetParameters(GetForm1Parameters(model));
                break;
            /**
             * Several other reports are in this switch.  All reports have the
             * same issue - all but one are removed for brevity.
             */
        }

        byte[] bytes = viewer.LocalReport.Render("PDF", null, out mimeType, out encoding, out extension, out streamIds, out warnings);

        return bytes;

        //return new byte[5] {5,6,7,8,9}; - used for troubleshooting.
    }


    public static List<ReportParameter> GetReport1Parameters(Request model)
    { 
        List<ReportParameter> rptParams = new List<ReportParameter>();

        //Start comment
        rptParams.Add(new ReportParameter("EmployeeFullName", string.Format("{0:NN}", model.Employee)));
        rptParams.Add(new ReportParameter("EmployeePhoneNumber", string.Format("{0:(###) ###-####}", Convert.ToInt64(model.Employee.PhoneNumber))));
        rptParams.Add(new ReportParameter("HrchyShortDesc", model.Employee.HrchyShortDesc));
        rptParams.Add(new ReportParameter("RequestDate", model.RequestDate.ToShortDateString()));
        rptParams.Add(new ReportParameter("RequestRequested", model.RequestRequestType));
        rptParams.Add(new ReportParameter("ReasonForRequest", model.RequestRequestReason));
        rptParams.Add(new ReportParameter("LogNumber", model.CaseId));

        if (!string.IsNullOrWhiteSpace(model.TimeSensitiveReason)) rptParams.Add(new ReportParameter("TimeSensitiveReason", model.TimeSensitiveReason));

        var lastAction = model.LastActionOfType(WorkflowStateActionType.EmployeeConfirmation);
        if (lastAction != null)
        {
            rptParams.Add(new ReportParameter("TodaysDate", lastAction.ActionDate.ToShortDateString()));
            rptParams.Add(new ReportParameter("EmpConfirmed", "true"));
        }
        else rptParams.Add(new ReportParameter("TodaysDate", DateTime.Now.ToShortDateString()));
        //end comment
        return rptParams;
    }

通过大量评论进出并推送到我们的服务器,我推断出以下内容:

  1. 据我所知,调用 GetReport1Parameters 时发生错误。在上面的代码中,我包含了一个开始和结束注释 - 我已经注释掉了中间的所有内容,只留下了列表初始化和返回语句(一个空列表)并且仍然收到错误。
  2. 我已经注释掉了对 GetReport1Parameters 的调用并返回了一个无意义的字节数组并且没有收到异常。
  3. 所有功能在 localhost 上都可以正常工作,当我单步执行这些功能时,所有变量似乎都正常。

我尝试做的事情来纠正这种情况: 1. 从 app.config 中删除连接字符串,以便应用程序必须转到 web.config 以获取正确的字符串(即使它们是相同的)。2. 注释掉代码的不同部分以确定问题区域。3、尝试调用GetReport1Parameters方法返回null,导致null引用异常。4. 尝试使用空参数列表调用GetReport1Parameters,导致上述错误。5. 尝试在没有参数的情况下运行报告(甚至不是空白列表),得到一个缺少参数的 ReportProcessingException。

一些附加信息:

  • 我们在 web.config 中使用模拟身份为应用程序使用服务帐户。该行在 localhost 上被注释掉,但在 IIS 上运行。
  • 所有其他数据库交互工作正常。
  • 我们所有的数据库交互都是使用 LINQ to SQL 完成的——模型是基于数据库表的对象,其中包含一些动态计算的附加信息。

我想要的结果是自动生成的文档和预览/打印文档都可以工作。我有一种感觉,这可能是我忽略的一些简单的事情,但我今天已经花了几个小时试图解决这个问题。

我想不出任何其他相关信息,但如果您有任何问题,我将非常乐意回答。

编辑:寻找解决方案的其他尝试:

  1. 尝试将 LINQ 延迟加载设置为 false。这导致的问题比解决的问题多。

  2. 实现了 IReportServerCredentials 并为 ReportViewer 的 ServerReport.ReportServerCredentials 分配了正确的数据库凭据。

  3. 将所有相关的报告参数分配给字典,然后在每个对象上调用 .ToString() 以确保从数据库中提取它。然后将字典中的这些字符串分配给报告参数,以便 ReportViewer 应该从字符串池接收数据,而不是从数据库中提取数据。

4

1 回答 1

0

即使您使用 ObjectDataSource 将数据传递给报表,报表查看器仍将调用Select方法,这反过来可能导致发生数据库访问。因此,即使看起来登录似乎是不必要的,您也需要深入了解随 ObjectDataSource 提供的数据访问方法才能确定。

您遇到的错误是由以下 Microsoft Connect 文章中描述的 Report Viewer 2010 中的错误引起的:

ReportViewer.LocalReport.Render 和 ReportViewer.LocalReport.SetParameters 将 ImpersonationLevel 更改为 None

虽然文章提到此问题应在 Service Pack 1 中修复,但似乎并非如此。我尚未验证此问题是否已在 Report Viewer 2012 中得到解决。

我通过更改数据访问层以将当前身份与 HttpContext 中的身份进行比较来解决该问题,并在必要时使用以下代码片段恢复它:

System.Security.Principal.IIdentity id = System.Web.HttpContext.Current.User.Identity
if (id.Name != System.Security.Principal.WindowsIdentity.GetCurrent().Name)
{
    context = (id as System.Security.Principal.WindowsIdentity).Impersonate()
}

我在连接到数据库之前执行此操作,并在连接打开后立即撤消它。

我对这种解决方法并不十分满意,主要是因为现在我的数据访问层正在引用 UI 层 (System.Web)。

于 2013-07-27T19:22:18.890 回答