如何使用oracle将水晶报告导出为pdf

时间:2012-04-09 20:50:20

标签: .net oracle crystal-reports

尝试导出为pdf时出现InvalidComObjectException错误。我认为它可能与连接到oracle有关,但不确定。 SetDatabaseLogon似乎更喜欢Sql Server。该报告调用数据的存储过程,并使用Business Objects XIR2服务。我也可以查看报告并连接设计师。

'CrystalDecisions.CrystalReports.Engine 13.0.2000.0 '.NET Framework 4.0的Crystal Reports

Imports CrystalDecisions.CrystalReports.Engine
Imports CrystalDecisions.ReportAppServer.DataDefModel
Imports CrystalDecisions.Shared
Imports CrystalDecisions.ReportSource

   Public Overloads Function CreateReport(ByVal reportFileName As String, ByVal reportTitle As String, ByVal outputDirectory As String, _
ByVal isFullPath As Boolean, ByVal outputFormat As OutputFormat, ByVal crystalParameter As CrystalParameter) As String


      Dim baseReportsSourcePath As String = "C:\ADSTAX\SUITES\RFL\Letters\"
      Dim baseReportsOutputPath As String = "C:\PDF_Exports\"

      Dim fullFilepath As String = baseReportsSourcePath + reportFileName

      'check report exists
      If File.Exists(fullFilepath) = False Then Throw New FileNotFoundException("File:" + fullFilepath + " does not exist", fullFilepath)

      Dim doc As ReportDocument = New ReportDocument()
      doc.Load(fullFilepath)

      'set parameters
      If crystalParameter.Name = CrystalParameterName.DistributionKey Then

         doc.SetParameterValue("DISTRIBUTIONKEY", crystalParameter.Value)

      ElseIf crystalParameter.Name = CrystalParameterName.RequestKey Then

         doc.SetParameterValue("REQUESTKEY", crystalParameter.Value)

      End If

      'user, pass, server, database
      doc.SetDatabaseLogon("user", "pass")


      'build full output filename
      Dim exportFileName As String = Path.GetFileNameWithoutExtension(reportFileName)
      exportFileName += Guid.NewGuid.ToString + ".pdf"
      Dim fullOuputFilePath As String = baseReportsOutputPath + exportFileName

      'export to pdf
      doc.ExportToDisk(CrystalDecisions.Shared.ExportFormatType.PortableDocFormat, fullOuputFilePath)

      'check report created
      If File.Exists(fullFilepath) = False Then
         Return String.Empty
      End If

      Return fullOuputFilePath

   End Function

以下错误:

System.Runtime.InteropServices.InvalidComObjectException was unhandled
  Message=COM object that has been separated from its underlying RCW cannot be used.
  Source=mscorlib
  StackTrace:
       at System.StubHelpers.StubHelpers.StubRegisterRCW(Object pThis, IntPtr pThread)
       at System.Runtime.InteropServices.ComTypes.IConnectionPoint.Unadvise(Int32 dwCookie)
       at CrystalDecisions.ReportAppServer.ISCDClientDocumentEvents_EventProvider.RemoveOnClosed(_ISCDClientDocumentEvents_OnClosedEventHandler handler)
       at CrystalDecisions.ReportAppServer.ISCDClientDocumentEvents_EventProvider.remove_OnClosed(_ISCDClientDocumentEvents_OnClosedEventHandler value)
       at CrystalDecisions.ReportAppServer.ReportClientDocumentWrapper.DisconnectEventRelay()
       at CrystalDecisions.ReportAppServer.ReportClientDocumentWrapper.InternalClose(Boolean bSetupForNextReport, Boolean bAutoClose)
       at CrystalDecisions.ReportAppServer.ReportClientDocumentWrapper.Dispose(Boolean bDisposeManaged)
       at System.ComponentModel.Component.Dispose()
       at CrystalDecisions.CrystalReports.Engine.ReportDocument.ClearCache(Boolean clearDocument)
       at CrystalDecisions.CrystalReports.Engine.ReportDocument.InternalClose(Boolean bSetupForNextReport)
       at CrystalDecisions.CrystalReports.Engine.ReportDocument.Close()
       at CrystalDecisions.CrystalReports.Engine.ReportDocument.ExitHandler(Object sender, EventArgs e)
  InnerException: 

4 个答案:

答案 0 :(得分:1)

在经历了许多水晶问题之后,这最终会起作用。 其他许多人都有信用。我主要发现了所需要的东西,拼凑在一起,其他人也在寻找解决方案。

您还需要添加一个引用:CrystalDecisions.ReportAppServer.DataDefModel.dll

Imports CrystalDecisions.ReportAppServer.DataDefModel

   Public Overloads Function CreateReport(ByVal reportFileName As String, ByVal reportTitle As String, ByVal outputDirectory As String, _
ByVal isFullPath As Boolean, ByVal outputFormat As OutputFormat, ByVal crystalParameter As CrystalParameter) As String


      Dim baseReportsSourcePath As String = "C:\data\ReportTemplates\Correspondence\" '"C:\ADSTAX\SUITES\RFL\Letters\"
      Dim baseReportsOutputPath As String = "C:\PDF_Exports\"

      Dim fullFilepath As String = baseReportsSourcePath + reportFileName

      'check report exists
      If File.Exists(fullFilepath) = False Then Throw New FileNotFoundException("File:" + fullFilepath + " does not exist", fullFilepath)

      'build crystal
      Dim startCreateDoc As Long = DateTime.Now.Ticks
      Dim doc As ReportDocument = New ReportDocument()

      Dim tsc As New TimeSpan(startCreateDoc - DateTime.Now.Ticks)
      Trace.WriteLine(fullFilepath + " Report create time:" + tsc.ToString)

      Dim startLoad As Long = DateTime.Now.Ticks
      doc.Load(fullFilepath)

      Dim ts As New TimeSpan(startLoad - DateTime.Now.Ticks)
      Trace.WriteLine(fullFilepath + " Report Load time:" + ts.ToString)


      CrystalLogin(doc, "service", "user", "password")

      'set parameters
      If crystalParameter.Name = CrystalParameterName.DistributionKey Then
         doc.ApplyParameters("DISTRIBUTIONKEY=" + crystalParameter.Value)
      ElseIf crystalParameter.Name = CrystalParameterName.RequestKey Then
         doc.ApplyParameters("REQUESTKEY=" + crystalParameter.Value)
      End If

      'build full output filename
      Dim exportFileName As String = Path.GetFileNameWithoutExtension(reportFileName)
      exportFileName += "_"
      exportFileName += Guid.NewGuid.ToString + ".pdf"
      Dim fullOuputFilePath As String = baseReportsOutputPath + exportFileName

      'export to pdf
      'doc.ExportToDisk(ExportFormatType.PortableDocFormat, fullOuputFilePath)
      Dim pdfOps As CrystalDecisions.Shared.PdfFormatOptions = CrystalDecisions.Shared.ExportOptions.CreatePdfFormatOptions

      Dim eo As New CrystalDecisions.Shared.ExportOptions
      eo.ExportFormatType = ExportFormatType.PortableDocFormat
      eo.ExportDestinationType = ExportDestinationType.DiskFile
      eo.ExportFormatOptions = pdfOps

      'Dim htmlOps As CrystalDecisions.Shared.HTMLFormatOptions = CrystalDecisions.Shared.ExportOptions.CreateHTMLFormatOptions
      'htmlOps.HTMLFileName = fullOuputFilePath
      'eo.ExportFormatOptions = htmlOps

      Dim dop As DiskFileDestinationOptions = CrystalDecisions.Shared.ExportOptions.CreateDiskFileDestinationOptions
      dop.DiskFileName = fullOuputFilePath

      eo.ExportDestinationOptions = dop

      doc.Export(eo)
      doc.Close()

      'check report created
      If File.Exists(fullFilepath) = False Then
         Return String.Empty
      End If

      Return fullOuputFilePath

   End Function

   Public Shared Sub CrystalLogin(mainInRD As ReportDocument, dataSource As String, userId As String, pwd As String)
      Try
         'now update logon info for all sub-reports
         If Not mainInRD.IsSubreport AndAlso mainInRD.Subreports IsNot Nothing AndAlso mainInRD.Subreports.Count > 0 Then
            For Each rd As ReportDocument In mainInRD.Subreports
               CrystalLogin(rd, dataSource, userId, pwd)
            Next
         End If
      Catch
      End Try
      'do the main reports database
      Dim logonInfo As TableLogOnInfo = Nothing
      For Each table As CrystalDecisions.CrystalReports.Engine.Table In mainInRD.Database.Tables
         logonInfo = table.LogOnInfo
         logonInfo.ConnectionInfo.ServerName = dataSource
         logonInfo.ConnectionInfo.DatabaseName = ""
         logonInfo.ConnectionInfo.UserID = userId
         logonInfo.ConnectionInfo.Password = pwd
         table.ApplyLogOnInfo(logonInfo)

   'This part was needed to support the oracle store procs we use to obtain the data
         Dim prop As PropertyInfo = Nothing
         prop = table.[GetType]().GetProperty("RasTable", BindingFlags.NonPublic Or BindingFlags.Instance)
         Dim rasTable As ISCRTable = Nothing
         rasTable = DirectCast(prop.GetValue(table, Nothing), ISCRTable)
         table.Location = rasTable.QualifiedName
         'Console.Out.WriteLine(table.Name)
         'Console.Out.WriteLine(rasTable.QualifiedName)
      Next

   End Sub

答案 1 :(得分:0)

我建议看一下这段代码:

Imports CrystalDecisions.CrystalReports.Engine
Imports CrystalDecisions.Shared
Public Class Form1
    Private Sub Button1_Click(ByVal sender As System.Object, _
    ByVal e As System.EventArgs) Handles Button1.Click
        Dim cryRpt As New ReportDocument
        Dim crtableLogoninfos As New TableLogOnInfos
        Dim crtableLogoninfo As New TableLogOnInfo
        Dim crConnectionInfo As New ConnectionInfo
        Dim CrTables As Tables
        Dim CrTable As Table

        cryRpt.Load("PUT CRYSTAL REPORT PATH HERE\CrystalReport1.rpt")

        With crConnectionInfo
            .ServerName = "YOUR SERVER NAME"
            .DatabaseName = "YOUR DATABASE NAME"
            .UserID = "YOUR DATABASE USERNAME"
            .Password = "YOUR DATABASE PASSWORD"
        End With

        CrTables = cryRpt.Database.Tables
        For Each CrTable In CrTables
            crtableLogoninfo = CrTable.LogOnInfo
            crtableLogoninfo.ConnectionInfo = crConnectionInfo
            CrTable.ApplyLogOnInfo(crtableLogoninfo)
        Next

        CrystalReportViewer1.ReportSource = cryRpt
        CrystalReportViewer1.Refresh()
    End Sub
End Class

来自http://vb.net-informations.com/crystal-report/vb.net_crystal_report_load_dynamically.htm

我可能会在星号所在的位置加上一个断点:

        For Each CrTable In CrTables
            *crtableLogoninfo = CrTable.LogOnInfo
            crtableLogoninfo.ConnectionInfo = crConnectionInfo
            CrTable.ApplyLogOnInfo(crtableLogoninfo)
        Next

然后分析现有的CrTable.TableLogonInfo

希望你可以继续使用它。

答案 2 :(得分:0)

  1. 执行数据集以填充oracle storedprocedure中的数据。确保数据集中的表名与设计报表的数据库对象的名称相同。(在这种情况下为存储过程名称)

  2. 不提供任何logoninfo。 这很重要

  3. 设置所有报告参数,但不提供任何数据过滤器,因为数据集包含已过滤的数据。

  4. 手动执行数据绑定。

    doc.SetDataSource = yourdatasource;

  5. 现在像往常一样继续报告导出。

    这是填充报告的推送模型方式。通常我使用oledb dataadapter为oracle设计报告。但是,如果您正在处理存储过程,那么Push模型是唯一的方法。设计人员可以访问oracle SP并填充报告,但在应用程序中您必须处理它,否则您将看到恼人的弹出窗口,询问存储过程参数值。

    这是一些代码

    OracleConnection cn = new OracleConnection("Data Source=yourdbname;User ID=someid;password=somepw;Pooling=true;Connection Lifetime=30;Min Pool Size=5;Max Pool Size=100");
    
    OracleParameter DETAILS = new OracleParameter();
    DETAILS.ParameterName = "DETAILS";
    DETAILS.Direction = ParameterDirection.Output;
    
    OracleParameter NN = new OracleParameter();
    NN.ParameterName = "PRODUCT";
    NN.Direction = ParameterDirection.Input;
    NN.Value = 1000; // Some product id
    
    OracleParameter DD = new OracleParameter();
    DD.ParameterName = "TRDATE";
    DD.Direction = ParameterDirection.Input;
    DD.Value = “09-DEC-2008”; // Some Date
    
    
    // for Oracle.DataAccess.Client use the following
    DETAILS.OracleDbType = OracleDbType.RefCursor;
    NN.OracleDbType = OracleDbType.Varchar2;
    DD.OracleDbType = OracleDbType.Date;
    
    
    // for System.Data.OracleClient use the following
    //DETAILS.OracleType = OracleType.Cursor;
    //NN.OracleType = OracleType.VarChar;
    //DD.OracleType = OracleType.DateTime;
    
    
    
    OracleCommand cmd = new OracleCommand("Myschemaname.GETSTOCK", cn);
    cmd.CommandType = CommandType.StoredProcedure;
    cmd.Parameters.Add(NN);
    cmd.Parameters.Add(DD);
    cmd.Parameters.Add(DETAILS);
    OracleDataAdapter da = new OracleDataAdapter(cmd);
    DataSet ds = new DataSet();
    da.Fill(ds,"GETSTOCK"); //Name must be same as Procedure
    ReportDocument rptDoc = new ReportDocument();
    rptDoc.Load(Server.MapPath("FINC//abc.rpt"));
    rptDoc.SetDataSource(ds);
    CRT.ReportSource = rptDoc; // CRT is the name of Crystal report viewer control
        // your export routine goes here
    

答案 3 :(得分:0)

ESchneider,

我怀疑问题不是由于您设置了连接细节或参数。为了确认我的怀疑,您可以将代码放入一个简单的Web或Windows窗体/页面,而不是导出只是尝试通过将CrystalReportViewer对象放到该页面上来查看报告。 (注意:这有一些先决条件,但是如果你在安装了完整Crystal的开发机器上,它应该很容易满足这些要求。)

假设您没有看到投诉(即它提示输入参数或数据库凭证),那么我将倾向于完全不同的理论。

线索在您的错误消息中:“无法使用已与其基础RCW分离的COM对象。”此错误不是Crystal Reports独有的。

我之前看到过这与垃圾收集有关(无论是自动化还是由于您的代码导致的处理/解构)或多线程相关的挑战。

我注意到你将此称为一个函数,它将PDF路径作为字符串返回。也许你已经将调用嵌套到一个独立于调用线程的线程中?也许您有其他代码可以触发其他可能导致对象处理或资源/锁定相关问题的事件,如批处理?

避免推测的一种方法是将所有这些放入一个全新的,简单的基本Windows应用程序中。除触发导出的绝对最小值外什么都不做。确保Windows应用程序只是写入本地文件夹并从本地目录中读取报表。我们不希望文件权限或网络问题(通过网络读/写)发生干扰。

祝你好运。