本地ReportViewer在不必要的情况下登录数据库失败

本文关键字:登录 数据库 失败 情况下 ReportViewer 不必要 本地 | 更新日期: 2023-09-27 18:19:58

我继承了一个使用Microsoft Web ReportViewer在本地运行小型报告的应用程序。我们的应用程序允许您通过单击特定按钮"预览/打印"报告,该按钮将用户路由到一个URL,该URL允许用户以PDF格式下载报告。我们最近收到了将这些PDF保存到我们数据库中的文档表中的要求。我已经能够在本地主机上成功地实现这一点;但是,当我将应用程序发布到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 Deferred Loading设置为false。这造成的问题比解决的问题还多。

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

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

本地ReportViewer在不必要的情况下登录数据库失败

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

您收到的错误是由Report Viewer 2010中的一个错误引起的,该错误在以下Microsoft Connect文章中进行了描述:

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)。